package modules import ( "fmt" "os" "os/exec" "path/filepath" "strings" "testing" "gsg/internal/core" "gsg/internal/gitx" ) // blobCtx commits files into a throwaway repository and returns a Ctx // pointing at it. Isolation matches internal/gitx's own test helper, so a // machine with commit.gpgsign=true globally behaves like CI. func blobCtx(t *testing.T, files map[string]string) *core.Ctx { t.Helper() dir := t.TempDir() env := []string{ "PATH=" + os.Getenv("PATH"), "HOME=" + dir, "GIT_CONFIG_NOSYSTEM=1", "GIT_AUTHOR_NAME=test", "GIT_AUTHOR_EMAIL=test@example.com", "GIT_COMMITTER_NAME=test", "GIT_COMMITTER_EMAIL=test@example.com", } git := func(args ...string) { t.Helper() cmd := exec.Command("git", append([]string{"-C", dir, "-c", "commit.gpgsign=false"}, args...)...) cmd.Env = env if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("git %v: %v\n%s", args, err, out) } } git("init", "-q", ".") for name, body := range files { if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil { t.Fatal(err) } } git("add", "-A") git("commit", "-qm", "fixture") repo, err := gitx.Open(dir) if err != nil { t.Fatal(err) } sha, err := repo.Resolve("HEAD") if err != nil { t.Fatal(err) } return &core.Ctx{Repo: repo, Ref: "HEAD", Commit: sha} } func blobAt(t *testing.T, c *core.Ctx, path string) Blob { t.Helper() c.Path = path v, err := blobMod(c) if err != nil { t.Fatalf("blob %q: %v", path, err) } b, ok := v.(Blob) if !ok { t.Fatalf("blob %q: got %T", path, v) } return b } // Past maxStructuredSize a config file is served as plain numbered lines: // no parse, no fold ranges, no anchor resolution -- and Oversize set so the // view can say why rather than silently dropping the tooling. func TestStructuredSizeGate(t *testing.T) { var big strings.Builder big.WriteString("items: &items\n") for i := 0; big.Len() <= maxStructuredSize; i++ { fmt.Fprintf(&big, " - name: item-%d\n value: %d\n", i, i) } small := "items: &items\n - name: a\ncopy: *items\n" c := blobCtx(t, map[string]string{"big.yaml": big.String(), "small.yaml": small}) b := blobAt(t, c, "big.yaml") if b.Lang != "yaml" { t.Errorf("Lang = %q, want yaml", b.Lang) } if !b.Oversize || b.Structured() { t.Errorf("Oversize = %v, Structured = %v; want true, false", b.Oversize, b.Structured()) } if b.ParseErr != nil { t.Errorf("oversize file was parsed anyway: %+v", b.ParseErr) } if len(b.Lines) == 0 { t.Fatal("oversize file rendered no lines") } for _, l := range b.Lines { if l.FoldTo != 0 || l.Depth != 0 || l.Key != "" || l.Anchor != nil || l.Ref != nil { t.Fatalf("line %d carries structure past the size limit: %+v", l.N, l) } } s := blobAt(t, c, "small.yaml") if !s.Structured() || s.Oversize { t.Fatalf("small file: Structured = %v, Oversize = %v", s.Structured(), s.Oversize) } if s.Lines[0].Anchor == nil || s.Lines[2].Ref == nil { t.Error("small file lost its anchor/alias annotations") } if s.Lines[0].FoldTo != 2 { t.Errorf("small file FoldTo = %d, want 2", s.Lines[0].FoldTo) } } // A non-config file is never structured, whatever its size. func TestPlainFileIsNotStructured(t *testing.T) { c := blobCtx(t, map[string]string{"main.go": "package main\n\nfunc main() {}\n"}) b := blobAt(t, c, "main.go") if b.Structured() || b.Oversize || b.Lang != "" { t.Errorf("go file: Lang = %q, Structured = %v, Oversize = %v", b.Lang, b.Structured(), b.Oversize) } }