package workspaceops import ( "context" "encoding/json" "io" "os" "path/filepath" "reflect" "strings" "testing" "github.com/aritmolab/thothii/tools/tht/internal/compose" "github.com/aritmolab/thothii/tools/tht/internal/config" ) type fakeRunner struct { run func(args []string, stdin string) (compose.Result, error) all [][]string stdins []string } func (r *fakeRunner) Run(_ context.Context, args []string, stdin io.Reader) (compose.Result, error) { payload := "" if stdin != nil { contents, err := io.ReadAll(stdin) if err != nil { return compose.Result{}, err } payload = string(contents) } r.all = append(r.all, append([]string(nil), args...)) r.stdins = append(r.stdins, payload) return r.run(args, payload) } func TestExecuteRunSendsOnlyTheOneShotEnvelope(t *testing.T) { installation := testInstallation(t) runner := &fakeRunner{run: func(args []string, stdin string) (compose.Result, error) { switch { case contains(args, "config", "--format", "json"): return compose.Result{Stdout: `{"services":{"core":{"image":"thothii-core:local"}}}`}, nil case reflect.DeepEqual(args, []string{"image", "inspect", "--format", "{{.Id}}", "thothii-core:local"}): return compose.Result{Stdout: "sha256:" + strings.Repeat("a", 64)}, nil case contains(args, "workspace-maintenance", "preprocess-run"): var envelope map[string]any if err := json.Unmarshal([]byte(stdin), &envelope); err != nil { t.Fatalf("stdin JSON = %q, err=%v", stdin, err) } if !reflect.DeepEqual(envelope, map[string]any{ "schemaVersion": float64(1), "workspaceId": "abc", }) { t.Fatalf("unexpected envelope: %#v", envelope) } return compose.Result{Stdout: successResult("preprocess run")}, nil default: t.Fatalf("unexpected Docker invocation: %#v", args) return compose.Result{}, nil } }} result, err := Execute( context.Background(), installation, runner, RunRequest{baseRequest: baseRequest{Workspace: "abc", JSON: true}}, ) if err != nil { t.Fatalf("Execute() error = %v", err) } if result.Status != "succeeded" || result.Operation != "preprocess run" { t.Fatalf("unexpected result: %#v", result) } } func TestParseAcceptsOnlyInspectAndCompletePreprocessing(t *testing.T) { for _, args := range [][]string{ {"inspect", "--workspace", "abc", "--json"}, {"preprocess", "run", "--workspace", "abc", "--json"}, {"preprocess", "clear", "--workspace", "abc", "--json"}, } { if _, err := Parse(args); err != nil { t.Fatalf("Parse(%v) error = %v", args, err) } } retired := [][]string{ {"preprocess", "dwh", "--workspace", "abc"}, {"preprocess", "evidence", "--workspace", "abc"}, {"preprocess", "run", "--workspace", "abc", "--resume", strings.Repeat("a", 32)}, {"preprocess", "run", "--workspace", "abc", "--dry-run"}, {"schema", "suggest-fks", "--workspace", "abc"}, {"schema", "check", "--workspace", "abc"}, {"schema", "accept", "--workspace", "abc"}, {"index-schema", "--workspace", "abc"}, {"vector", "inspect", "--workspace", "abc"}, {"vector", "rebuild", "--workspace", "abc"}, } for _, args := range retired { if _, err := Parse(args); err == nil { t.Fatalf("retired command accepted: %v", args) } } } func testInstallation(t *testing.T) config.Installation { t.Helper() temporaryRoot, err := filepath.EvalSymlinks(os.TempDir()) if err != nil { t.Fatal(err) } root, err := os.MkdirTemp(temporaryRoot, "tht-workspaceops-") if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.RemoveAll(root) }) project := filepath.Join(root, "project") if err := os.MkdirAll(filepath.Join(project, "deploy"), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(project, "compose.yaml"), []byte("services: {}\n"), 0o600); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(project, "deploy", "compose.local.yaml"), []byte("services: {}\n"), 0o600); err != nil { t.Fatal(err) } envFile := filepath.Join(root, "installation.env") if err := os.WriteFile(envFile, []byte("SAFE_VALUE=1\n"), 0o600); err != nil { t.Fatal(err) } return config.Installation{ Path: filepath.Join(root, "thothii-installation.yaml"), Profile: "local", ProjectDirectory: project, EnvFile: envFile, } } func contains(values []string, sequence ...string) bool { for start := range values { if start+len(sequence) <= len(values) && reflect.DeepEqual(values[start:start+len(sequence)], sequence) { return true } } return false } func successResult(operation string) string { return `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"abc","workspaceRevision":"1234567890abcdef1234567890abcdef12345678","descriptorBlob":"sha256:` + strings.Repeat("f", 64) + `","operation":"` + operation + `","completedStages":[]}` }