package cli import ( "bytes" "encoding/json" "os" "path/filepath" "strings" "testing" ) // run executes the command tree with args and returns stdout, stderr and the error. func run(t *testing.T, args ...string) (string, string, error) { t.Helper() var stdout, stderr bytes.Buffer cmd := NewRootCmd() cmd.SetOut(&stdout) cmd.SetErr(&stderr) cmd.SetArgs(args) err := cmd.Execute() return stdout.String(), stderr.String(), err } func TestGenerate(t *testing.T) { out := filepath.Join(t.TempDir(), "tenders", "ocds_gen.go") if err := os.MkdirAll(filepath.Dir(out), 0o755); err != nil { t.Fatal(err) } if _, stderr, err := run(t, "generate", "-c", "testdata/widgets.json", "-o", out); err != nil { t.Fatalf("generate: %v\n%s", err, stderr) } raw, err := os.ReadFile(out) if err != nil { t.Fatal(err) } // gofmt aligns struct columns, so compare with whitespace collapsed. src := strings.Join(strings.Fields(string(raw)), " ") for _, want := range []string{ "package tenders", // derived from the output directory "Date time.Time", // required date-time is not a pointer "ID ID `json:\"id\"`", // ["string","integer"] "Widgets []Widget", // extension field on a core type "Count *int", // extension definition "Releases []Release", // external $ref to the release schema "Widgets (declared)", // header records resolution "Orphan (dependency)", } { if !strings.Contains(src, want) { t.Errorf("generated code is missing %q", want) } } } func TestGenerateStdoutNeedsPackage(t *testing.T) { _, _, err := run(t, "generate", "-c", "testdata/core.json", "-o", "-") if err == nil || !strings.Contains(err.Error(), "--package") { t.Fatalf("want --package error, got %v", err) } stdout, _, err := run(t, "generate", "-c", "testdata/core.json", "-o", "-", "-p", "core") if err != nil || !strings.HasPrefix(stdout, "// Code generated") { t.Fatalf("stdout generation failed: %v", err) } } func TestCheck(t *testing.T) { out := filepath.Join(t.TempDir(), "ocds_gen.go") args := []string{"generate", "-q", "-c", "testdata/core.json", "-o", out, "-p", "core"} if _, _, err := run(t, append(args, "--check")...); err == nil || !strings.Contains(err.Error(), "does not exist") { t.Fatalf("missing file: want error, got %v", err) } if _, _, err := run(t, args...); err != nil { t.Fatal(err) } if _, _, err := run(t, append(args, "--check")...); err != nil { t.Fatalf("fresh file: want no error, got %v", err) } f, _ := os.OpenFile(out, os.O_APPEND|os.O_WRONLY, 0) f.WriteString("// hand edit\n") f.Close() if _, _, err := run(t, append(args, "--check")...); err == nil || !strings.Contains(err.Error(), "out of date") { t.Fatalf("edited file: want out-of-date error, got %v", err) } } func TestStrict(t *testing.T) { out := filepath.Join(t.TempDir(), "ocds_gen.go") args := []string{"generate", "-c", "testdata/widgets.json", "-o", out, "-p", "x"} // Gadget is defined by the Orphan extension but nothing refers to it. _, stderr, err := run(t, args...) if err != nil { t.Fatal(err) } if !strings.Contains(stderr, "definition Gadget is never referenced") { t.Errorf("want unreachable warning, got stderr:\n%s", stderr) } if _, _, err := run(t, append(args, "--strict")...); err == nil { t.Fatal("--strict: want error") } if _, stderr, _ := run(t, append(args, "-q")...); stderr != "" { t.Errorf("--quiet: want no stderr, got:\n%s", stderr) } } func TestResolveJSON(t *testing.T) { stdout, _, err := run(t, "resolve", "-c", "testdata/widgets.json", "--json") if err != nil { t.Fatal(err) } var rows []struct { Name string `json:"name"` Declared bool `json:"declared"` } if err := json.Unmarshal([]byte(stdout), &rows); err != nil { t.Fatal(err) } if len(rows) != 2 || rows[0].Name != "Orphan" || rows[0].Declared || rows[1].Name != "Widgets" || !rows[1].Declared { t.Fatalf("want [Orphan (dependency), Widgets (declared)], got %+v", rows) } } func TestSchema(t *testing.T) { stdout, _, err := run(t, "schema", "-q", "-c", "testdata/widgets.json") if err != nil { t.Fatal(err) } // Key order is preserved, and the extension's patch is merged in. if !strings.Contains(stdout, `"widgets"`) || strings.Index(stdout, `"ocid"`) > strings.Index(stdout, `"tender"`) { t.Fatalf("unexpected merged schema:\n%s", stdout) } if _, _, err := run(t, "schema", "--kind", "record", "-c", "testdata/core.json"); err == nil { t.Fatal("--kind record: want error") } } func TestPackageFromPath(t *testing.T) { for in, want := range map[string]string{ "internal/tenders/ocds_gen.go": "tenders", "profiles/uk/ocds_gen.go": "uk", "pkg/eu-profile/x.go": "euprofile", } { got, err := packageFromPath(in) if err != nil || got != want { t.Errorf("packageFromPath(%q) = %q, %v; want %q", in, got, err, want) } } if _, err := packageFromPath("pkg/func/x.go"); err == nil { t.Error("keyword directory: want error") } }