forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
1package workflow 2 3import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7) 8 9func TestUnmarshalWorkflowWithBranch(t *testing.T) { 10 yamlData := ` 11when: 12 - event: ["push", "pull_request"] 13 branch: ["main", "develop"]` 14 15 wf, err := FromFile("test.yml", []byte(yamlData)) 16 assert.NoError(t, err, "YAML should unmarshal without error") 17 18 assert.Len(t, wf.When, 1, "Should have one constraint") 19 assert.ElementsMatch(t, []string{"main", "develop"}, wf.When[0].Branch) 20 assert.ElementsMatch(t, []string{"push", "pull_request"}, wf.When[0].Event) 21 22 assert.False(t, wf.CloneOpts.Skip, "Skip should default to false") 23} 24 25func TestUnmarshalCloneFalse(t *testing.T) { 26 yamlData := ` 27when: 28 - event: pull_request_close 29 30clone: 31 skip: true 32` 33 34 wf, err := FromFile("test.yml", []byte(yamlData)) 35 assert.NoError(t, err) 36 37 assert.ElementsMatch(t, []string{"pull_request_close"}, wf.When[0].Event) 38 39 assert.True(t, wf.CloneOpts.Skip, "Skip should be false") 40} 41 42func TestUnmarshalWorkflowWithTags(t *testing.T) { 43 yamlData := ` 44when: 45 - event: ["push"] 46 tag: ["v*", "release-*"]` 47 48 wf, err := FromFile("test.yml", []byte(yamlData)) 49 assert.NoError(t, err, "YAML should unmarshal without error") 50 51 assert.Len(t, wf.When, 1, "Should have one constraint") 52 assert.ElementsMatch(t, []string{"v*", "release-*"}, wf.When[0].Tag) 53 assert.ElementsMatch(t, []string{"push"}, wf.When[0].Event) 54} 55 56func TestUnmarshalWorkflowWithBranchAndTag(t *testing.T) { 57 yamlData := ` 58when: 59 - event: ["push"] 60 branch: ["main", "develop"] 61 tag: ["v*"]` 62 63 wf, err := FromFile("test.yml", []byte(yamlData)) 64 assert.NoError(t, err, "YAML should unmarshal without error") 65 66 assert.Len(t, wf.When, 1, "Should have one constraint") 67 assert.ElementsMatch(t, []string{"main", "develop"}, wf.When[0].Branch) 68 assert.ElementsMatch(t, []string{"v*"}, wf.When[0].Tag) 69} 70 71func TestMatchesPattern(t *testing.T) { 72 tests := []struct { 73 name string 74 input string 75 patterns []string 76 expected bool 77 }{ 78 {"exact match", "main", []string{"main"}, true}, 79 {"exact match in list", "develop", []string{"main", "develop"}, true}, 80 {"no match", "feature", []string{"main", "develop"}, false}, 81 {"wildcard prefix", "v1.0.0", []string{"v*"}, true}, 82 {"wildcard suffix", "release-1.0", []string{"*-1.0"}, true}, 83 {"wildcard middle", "feature-123-test", []string{"feature-*-test"}, true}, 84 {"double star prefix", "release-1.0.0", []string{"release-**"}, true}, 85 {"double star with slashes", "release/1.0/hotfix", []string{"release/**"}, true}, 86 {"double star matches multiple levels", "foo/bar/baz/qux", []string{"foo/**"}, true}, 87 {"double star no match", "feature/test", []string{"release/**"}, false}, 88 {"no patterns matches nothing", "anything", []string{}, false}, 89 {"pattern doesn't match", "v1.0.0", []string{"release-*"}, false}, 90 {"complex pattern", "release/v1.2.3", []string{"release/*"}, true}, 91 {"single star stops at slash", "release/1.0/hotfix", []string{"release/*"}, false}, 92 } 93 94 for _, tt := range tests { 95 t.Run(tt.name, func(t *testing.T) { 96 result, _ := matchesPattern(tt.input, tt.patterns) 97 assert.Equal(t, tt.expected, result, "matchesPattern(%q, %v) should be %v", tt.input, tt.patterns, tt.expected) 98 }) 99 } 100} 101 102func TestConstraintMatchRef_Branches(t *testing.T) { 103 tests := []struct { 104 name string 105 constraint Constraint 106 ref string 107 expected bool 108 }{ 109 { 110 name: "exact branch match", 111 constraint: Constraint{Branch: []string{"main"}}, 112 ref: "refs/heads/main", 113 expected: true, 114 }, 115 { 116 name: "branch glob match", 117 constraint: Constraint{Branch: []string{"feature-*"}}, 118 ref: "refs/heads/feature-123", 119 expected: true, 120 }, 121 { 122 name: "branch no match", 123 constraint: Constraint{Branch: []string{"main"}}, 124 ref: "refs/heads/develop", 125 expected: false, 126 }, 127 { 128 name: "no constraints matches nothing", 129 constraint: Constraint{}, 130 ref: "refs/heads/anything", 131 expected: false, 132 }, 133 } 134 135 for _, tt := range tests { 136 t.Run(tt.name, func(t *testing.T) { 137 result, _ := tt.constraint.MatchRef(tt.ref) 138 assert.Equal(t, tt.expected, result, "MatchRef should return %v for ref %q", tt.expected, tt.ref) 139 }) 140 } 141} 142 143func TestConstraintMatchRef_Tags(t *testing.T) { 144 tests := []struct { 145 name string 146 constraint Constraint 147 ref string 148 expected bool 149 }{ 150 { 151 name: "exact tag match", 152 constraint: Constraint{Tag: []string{"v1.0.0"}}, 153 ref: "refs/tags/v1.0.0", 154 expected: true, 155 }, 156 { 157 name: "tag glob match", 158 constraint: Constraint{Tag: []string{"v*"}}, 159 ref: "refs/tags/v1.2.3", 160 expected: true, 161 }, 162 { 163 name: "tag glob with pattern", 164 constraint: Constraint{Tag: []string{"release-*"}}, 165 ref: "refs/tags/release-2024", 166 expected: true, 167 }, 168 { 169 name: "tag no match", 170 constraint: Constraint{Tag: []string{"v*"}}, 171 ref: "refs/tags/release-1.0", 172 expected: false, 173 }, 174 { 175 name: "tag not matched when only branch constraint", 176 constraint: Constraint{Branch: []string{"main"}}, 177 ref: "refs/tags/v1.0.0", 178 expected: false, 179 }, 180 } 181 182 for _, tt := range tests { 183 t.Run(tt.name, func(t *testing.T) { 184 result, _ := tt.constraint.MatchRef(tt.ref) 185 assert.Equal(t, tt.expected, result, "MatchRef should return %v for ref %q", tt.expected, tt.ref) 186 }) 187 } 188} 189 190func TestConstraintMatchRef_Combined(t *testing.T) { 191 tests := []struct { 192 name string 193 constraint Constraint 194 ref string 195 expected bool 196 }{ 197 { 198 name: "matches branch in combined constraint", 199 constraint: Constraint{Branch: []string{"main"}, Tag: []string{"v*"}}, 200 ref: "refs/heads/main", 201 expected: true, 202 }, 203 { 204 name: "matches tag in combined constraint", 205 constraint: Constraint{Branch: []string{"main"}, Tag: []string{"v*"}}, 206 ref: "refs/tags/v1.0.0", 207 expected: true, 208 }, 209 { 210 name: "no match in combined constraint", 211 constraint: Constraint{Branch: []string{"main"}, Tag: []string{"v*"}}, 212 ref: "refs/heads/develop", 213 expected: false, 214 }, 215 { 216 name: "glob patterns in combined constraint - branch", 217 constraint: Constraint{Branch: []string{"release-*"}, Tag: []string{"v*"}}, 218 ref: "refs/heads/release-2024", 219 expected: true, 220 }, 221 { 222 name: "glob patterns in combined constraint - tag", 223 constraint: Constraint{Branch: []string{"release-*"}, Tag: []string{"v*"}}, 224 ref: "refs/tags/v2.0.0", 225 expected: true, 226 }, 227 } 228 229 for _, tt := range tests { 230 t.Run(tt.name, func(t *testing.T) { 231 result, _ := tt.constraint.MatchRef(tt.ref) 232 assert.Equal(t, tt.expected, result, "MatchRef should return %v for ref %q", tt.expected, tt.ref) 233 }) 234 } 235} 236 237func TestConstraintMatchBranch_GlobPatterns(t *testing.T) { 238 tests := []struct { 239 name string 240 constraint Constraint 241 branch string 242 expected bool 243 }{ 244 { 245 name: "exact match", 246 constraint: Constraint{Branch: []string{"main"}}, 247 branch: "main", 248 expected: true, 249 }, 250 { 251 name: "glob match", 252 constraint: Constraint{Branch: []string{"feature-*"}}, 253 branch: "feature-123", 254 expected: true, 255 }, 256 { 257 name: "no match", 258 constraint: Constraint{Branch: []string{"main"}}, 259 branch: "develop", 260 expected: false, 261 }, 262 { 263 name: "multiple patterns with match", 264 constraint: Constraint{Branch: []string{"main", "release-*"}}, 265 branch: "release-1.0", 266 expected: true, 267 }, 268 } 269 270 for _, tt := range tests { 271 t.Run(tt.name, func(t *testing.T) { 272 result, _ := tt.constraint.MatchBranch(tt.branch) 273 assert.Equal(t, tt.expected, result, "MatchBranch should return %v for branch %q", tt.expected, tt.branch) 274 }) 275 } 276} 277 278func TestConstraintMatchTag_GlobPatterns(t *testing.T) { 279 tests := []struct { 280 name string 281 constraint Constraint 282 tag string 283 expected bool 284 }{ 285 { 286 name: "exact match", 287 constraint: Constraint{Tag: []string{"v1.0.0"}}, 288 tag: "v1.0.0", 289 expected: true, 290 }, 291 { 292 name: "glob match", 293 constraint: Constraint{Tag: []string{"v*"}}, 294 tag: "v2.3.4", 295 expected: true, 296 }, 297 { 298 name: "no match", 299 constraint: Constraint{Tag: []string{"v*"}}, 300 tag: "release-1.0", 301 expected: false, 302 }, 303 { 304 name: "multiple patterns with match", 305 constraint: Constraint{Tag: []string{"v*", "release-*"}}, 306 tag: "release-2024", 307 expected: true, 308 }, 309 { 310 name: "empty tag list matches nothing", 311 constraint: Constraint{Tag: []string{}}, 312 tag: "v1.0.0", 313 expected: false, 314 }, 315 } 316 317 for _, tt := range tests { 318 t.Run(tt.name, func(t *testing.T) { 319 result, _ := tt.constraint.MatchTag(tt.tag) 320 assert.Equal(t, tt.expected, result, "MatchTag should return %v for tag %q", tt.expected, tt.tag) 321 }) 322 } 323}