Files
ThothII/tools/tht/cmd/tht/main_task6_test.go
T

108 lines
4.5 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"testing"
)
// Catches version regressing into a descriptor-dependent operator command.
func TestVersionCommandIsDescriptorFreeAndMachineReadable(t *testing.T) {
projectRoot := newProjectWithoutInstallation(t)
previousDirectory, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(filepath.Join(projectRoot, "frontend")); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = os.Chdir(previousDirectory) })
var stdout, stderr bytes.Buffer
if code := run(context.Background(), []string{"version", "--json"}, &stdout, &stderr); code != 0 {
t.Fatalf("run(version --json) = %d, stderr = %q", code, stderr.String())
}
var value struct {
Version string `json:"version"`
Commit string `json:"commit"`
OS string `json:"os"`
Arch string `json:"arch"`
}
decoder := json.NewDecoder(&stdout)
if err := decoder.Decode(&value); err != nil || value.Version == "" || value.Commit == "" || value.OS == "" || value.Arch == "" {
t.Fatalf("version JSON = %q, decoded %#v, err = %v", stdout.String(), value, err)
}
if err := decoder.Decode(&struct{}{}); err != io.EOF {
t.Fatalf("version stdout contains more than one JSON document: %q", stdout.String())
}
}
// Catches doctor --json writing a banner, progress, or Docker diagnostics to stdout.
func TestDoctorJSONWritesOnlyOneReportDocument(t *testing.T) {
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
fixture.setEnvironment(t)
fullHealth := `[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"},{"Service":"qdrant","State":"running","Health":"healthy"},{"Service":"embedding","State":"running","Health":"healthy"},{"Service":"embedding-model-init","State":"exited","ExitCode":0}]`
t.Setenv("THT_FAKE_PS", fullHealth)
var stdout, stderr bytes.Buffer
code := run(context.Background(), []string{"--installation", fixture.installationPath, "doctor", "--json"}, &stdout, &stderr)
if code != 0 && code != 1 {
t.Fatalf("run(doctor --json) = %d, stderr = %q", code, stderr.String())
}
var report struct {
OK *bool `json:"ok"`
Checks []json.RawMessage `json:"checks"`
}
decoder := json.NewDecoder(&stdout)
if err := decoder.Decode(&report); err != nil || report.OK == nil || report.Checks == nil {
t.Fatalf("doctor JSON = %q, decoded %#v, err = %v", stdout.String(), report, err)
}
if err := decoder.Decode(&struct{}{}); err != io.EOF {
t.Fatalf("doctor stdout contains more than one JSON document: %q", stdout.String())
}
if strings.Contains(stdout.String(), "Doctor checks passed") || strings.Contains(stdout.String(), "tht:") {
t.Fatalf("doctor JSON stdout contains presentation text: %q", stdout.String())
}
}
// Catches start --build being parsed as an argument but never reaching the shared lifecycle runner.
func TestStartBuildOptionBuildsBeforeUpWhilePlainStartDoesNotBuild(t *testing.T) {
fullHealth := `[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"},{"Service":"qdrant","State":"running","Health":"healthy"},{"Service":"embedding","State":"running","Health":"healthy"},{"Service":"embedding-model-init","State":"exited","ExitCode":0}]`
for _, test := range []struct {
name string
args []string
wantBuild bool
}{
{name: "plain", args: []string{"start"}, wantBuild: false},
{name: "build", args: []string{"start", "--build"}, wantBuild: true},
} {
t.Run(test.name, func(t *testing.T) {
fixture := newCLIFixture(t, "SAFE_VALUE=1\n")
fixture.setEnvironment(t)
t.Setenv("THT_FAKE_PS", fullHealth)
var stdout, stderr bytes.Buffer
if code := run(context.Background(), append([]string{"--installation", fixture.installationPath}, test.args...), &stdout, &stderr); code != 0 {
t.Fatalf("run(%v) = %d, stderr = %q", test.args, code, stderr.String())
}
calls := fixture.invocations(t)
joined := make([]string, 0, len(calls))
for _, call := range calls {
joined = append(joined, strings.Join(call, " "))
}
sequence := strings.Join(joined, "\n")
hasBuild := strings.Contains(sequence, " build")
if hasBuild != test.wantBuild {
t.Fatalf("start calls = %s, build present = %t, want %t", sequence, hasBuild, test.wantBuild)
}
if test.wantBuild && strings.Index(sequence, " build") > strings.Index(sequence, " up --detach --remove-orphans") {
t.Fatalf("start calls = %s, want build before up", sequence)
}
})
}
}