diff --git a/cmd/metis/remote_cmd_test.go b/cmd/metis/remote_cmd_test.go index 8bcee32..4f70dbb 100644 --- a/cmd/metis/remote_cmd_test.go +++ b/cmd/metis/remote_cmd_test.go @@ -1,6 +1,12 @@ package main -import "testing" +import ( + "bytes" + "io" + "os" + "strings" + "testing" +) func TestOrasPushInvocationUsesRelativeWorkspacePaths(t *testing.T) { dir, args, err := orasPushInvocation("registry.bstein.dev/metis/titan-13:20260331t235724z", "/workspace/build/titan-13.img.xz", "/workspace/build/metadata.json") @@ -32,3 +38,36 @@ func TestHumanHostPathMapsMountedTmpBackToHostTmp(t *testing.T) { t.Fatalf("expected /tmp/metis-flash-test, got %q", got) } } + +func TestNewProgressEmitterWritesStructuredMarker(t *testing.T) { + origStdout := os.Stdout + reader, writer, err := os.Pipe() + if err != nil { + t.Fatalf("pipe: %v", err) + } + os.Stdout = writer + defer func() { + os.Stdout = origStdout + }() + + emitter := newProgressEmitter("flash", 92, 98, "Writing the latest image for titan-12", true) + emitter(1024, 2048) + + if err := writer.Close(); err != nil { + t.Fatalf("close writer: %v", err) + } + var output bytes.Buffer + if _, err := io.Copy(&output, reader); err != nil { + t.Fatalf("read progress output: %v", err) + } + got := output.String() + if !strings.Contains(got, "METIS_PROGRESS ") { + t.Fatalf("expected structured progress prefix, got %q", got) + } + if !strings.Contains(got, `"stage":"flash"`) { + t.Fatalf("expected flash stage marker, got %q", got) + } + if !strings.Contains(got, `"written_bytes":1024`) || !strings.Contains(got, `"total_bytes":2048`) { + t.Fatalf("expected byte counters in progress marker, got %q", got) + } +} diff --git a/pkg/service/app_job_test.go b/pkg/service/app_job_test.go new file mode 100644 index 0000000..fe2cf7d --- /dev/null +++ b/pkg/service/app_job_test.go @@ -0,0 +1,27 @@ +package service + +import ( + "errors" + "strings" + "testing" +) + +func TestReserveJobRejectsDuplicateActiveNodeJobs(t *testing.T) { + app := newTestApp(t) + active := app.newJob("replace", "titan-15", "titan-22", "/dev/sdk") + + _, err := app.reserveJob("build", "titan-15", "", "") + if err == nil { + t.Fatal("expected duplicate job reservation to fail") + } + var activeErr *activeNodeJobError + if !errors.As(err, &activeErr) { + t.Fatalf("expected activeNodeJobError, got %T", err) + } + if activeErr.JobID != active.ID || activeErr.Kind != "replace" { + t.Fatalf("unexpected active job conflict: %#v", activeErr) + } + if !strings.Contains(err.Error(), active.ID) { + t.Fatalf("expected error to mention active job id %s, got %q", active.ID, err.Error()) + } +}