1// SPDX-License-Identifier: MIT
2
3package action
4
5import (
6 "os"
7 "path/filepath"
8 "testing"
9)
10
11func FuzzParse(f *testing.F) {
12 files, _ := filepath.Glob("testdata/cases/*.yml")
13 for _, name := range files {
14 if data, err := os.ReadFile(name); err == nil {
15 f.Add(data)
16 }
17 }
18 f.Add([]byte("name: a\ndescription: d\nruns:\n using: node20\n main: m.js\n"))
19 f.Add([]byte(""))
20 f.Add([]byte("\x00\xff"))
21 f.Add([]byte("runs: &a\n using: *a\n"))
22 f.Add([]byte("runs:\n using: composite\n steps:\n - uses: $/x@\n"))
23 f.Fuzz(func(t *testing.T, data []byte) {
24 a, errs := Parse("action.yml", data)
25 if (a == nil) == (len(errs) == 0) {
26 t.Fatalf("action nil = %v with %d errors", a == nil, len(errs))
27 }
28 if len(errs) > maxErrors {
29 t.Fatalf("%d errors, the limit is %d", len(errs), maxErrors)
30 }
31 for _, e := range errs {
32 _ = e.Error()
33 }
34 if a != nil {
35 if a.Runs == nil {
36 t.Fatal("no runs")
37 }
38 for _, s := range a.Runs.Steps {
39 if s.ID == "" || s.If == nil {
40 t.Fatalf("step without id or condition: %+v", s)
41 }
42 if s.Uses != nil {
43 _, _ = ParseUses(s.Uses.Value)
44 }
45 }
46 _ = a.Runs.HasPre()
47 _ = a.Runs.HasPost()
48 if a.Runs.Kind == RunsDocker {
49 _, _ = ParseImage(a.Runs.Image)
50 }
51 }
52 _ = Lint("action.yml", data)
53 })
54}
55
56func FuzzParseUses(f *testing.F) {
57 for _, s := range []string{"actions/checkout@v4", "./x", "docker://a", "$/a", "a@b@c", "", "//", `\@`, "a/b@"} {
58 f.Add(s)
59 }
60 f.Fuzz(func(t *testing.T, s string) {
61 u, err := ParseUses(s)
62 if (err == nil) != (u.Kind != 0) {
63 t.Fatalf("%q: kind %d with error %v", s, u.Kind, err)
64 }
65 if u.Kind == UsesRepository && (u.Owner == "" || u.Repo == "" || u.Ref == "") {
66 t.Fatalf("%q: %+v", s, u)
67 }
68 _, _ = ParseImage(s)
69 })
70}