From b2f096a8560f26574988becc51ee070dfa14333c Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Wed, 16 Sep 2026 08:31:34 -0700 Subject: [PATCH 1/2] fix(e2e): follow job runs by default --- e2e/server_execution_test.go | 4 ++-- e2e/server_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/server_execution_test.go b/e2e/server_execution_test.go index 589ac01..929d78f 100644 --- a/e2e/server_execution_test.go +++ b/e2e/server_execution_test.go @@ -71,7 +71,7 @@ workloads: // A legacy compose-run container can survive a crash without durable labels. // The first durable activation must reclaim this stopped, owned container. s.run(t, "docker compose -p "+name+" --project-directory "+root+"/current -f "+root+"/current/compose.yaml run --no-deps --name "+name+"-refresh-1 refresh true") - if out, err := s.ob(t, dir, "job", "run", "refresh", "--input", "SOURCE=custom", "--wait"); err == nil { + if out, err := s.ob(t, dir, "job", "run", "refresh", "--input", "SOURCE=custom"); err == nil { t.Fatalf("index should fail before allow marker: %s", out) } list := s.mustOb(t, dir, "execution", "list", "--output", "json") @@ -114,7 +114,7 @@ workloads: // Interrupted work is retained independently of the host journal. A new // failed execution can be inspected after its runner and notifier exit. s.run(t, "rm -f "+base+"/data/allow") - _, _ = s.ob(t, dir, "job", "run", "refresh", "--wait") + _, _ = s.ob(t, dir, "job", "run", "refresh") pins := s.run(t, "/usr/bin/python3 "+root+"/schedule/execution-v1.py pins "+root) if strings.TrimSpace(pins) == "" { t.Fatal("failed execution lost its durable release reference") diff --git a/e2e/server_test.go b/e2e/server_test.go index 3ef0a0d..2aebd2a 100644 --- a/e2e/server_test.go +++ b/e2e/server_test.go @@ -257,7 +257,7 @@ ExecStart=/usr/bin/docker compose -p observer -f /var/lib/ob/observer/current/co // An operator run with an input override reaches the container as its // environment, is journaled with the operator, and shows up as such. - manual := s.mustOb(t, dir, "job", "run", "input-chore", "--input", "GREETING=hello", "--wait", "--output", "json") + manual := s.mustOb(t, dir, "job", "run", "input-chore", "--input", "GREETING=hello", "--output", "json") for _, want := range []string{`"GREETING": "hello"`, `"outcome": "success"`, `"trigger": "operator"`} { if !strings.Contains(manual, want) { t.Fatalf("operator run result is missing %q:\n%s", want, manual) @@ -334,7 +334,7 @@ HTTPServer(("127.0.0.1", 18080), Handler).handle_request() // clears the failure from `ob status`; only a later successful run // does. An operator run with the input that makes the job finish in time // is that run, and it must leave status green for the steps after. - s.mustOb(t, dir, "job", "run", "timeout-chore", "--input", "SLEEP=0", "--wait") + s.mustOb(t, dir, "job", "run", "timeout-chore", "--input", "SLEEP=0") if cleared := s.mustOb(t, dir, "status"); !strings.Contains(cleared, "schedule timeout-chore active") { t.Fatalf("a successful operator run did not clear the recorded timeout:\n%s", cleared) } From 79cd66571ff06bc71c43b3ff9c165ed48c35b15c Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Wed, 16 Sep 2026 08:46:14 -0700 Subject: [PATCH 2/2] test(e2e): exercise operator job approval --- e2e/server_execution_test.go | 8 +++++--- e2e/server_test.go | 9 +++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/e2e/server_execution_test.go b/e2e/server_execution_test.go index 929d78f..1d0e3c4 100644 --- a/e2e/server_execution_test.go +++ b/e2e/server_execution_test.go @@ -71,7 +71,7 @@ workloads: // A legacy compose-run container can survive a crash without durable labels. // The first durable activation must reclaim this stopped, owned container. s.run(t, "docker compose -p "+name+" --project-directory "+root+"/current -f "+root+"/current/compose.yaml run --no-deps --name "+name+"-refresh-1 refresh true") - if out, err := s.ob(t, dir, "job", "run", "refresh", "--input", "SOURCE=custom"); err == nil { + if out, err := s.obInput(t, dir, s.obHome(t), "y\n", "job", "run", "refresh", "--input", "SOURCE=custom"); err == nil { t.Fatalf("index should fail before allow marker: %s", out) } list := s.mustOb(t, dir, "execution", "list", "--output", "json") @@ -114,7 +114,7 @@ workloads: // Interrupted work is retained independently of the host journal. A new // failed execution can be inspected after its runner and notifier exit. s.run(t, "rm -f "+base+"/data/allow") - _, _ = s.ob(t, dir, "job", "run", "refresh") + _, _ = s.obInput(t, dir, s.obHome(t), "y\n", "job", "run", "refresh") pins := s.run(t, "/usr/bin/python3 "+root+"/schedule/execution-v1.py pins "+root) if strings.TrimSpace(pins) == "" { t.Fatal("failed execution lost its durable release reference") @@ -122,7 +122,9 @@ workloads: // A killed activation must be inspectable and resume only its interrupted // step. Concurrent resume and deployment coordination must refuse live work. s.run(t, "touch "+base+"/data/hold") - s.mustOb(t, dir, "job", "run", "refresh") + if out, err := s.obInput(t, dir, s.obHome(t), "y\n", "job", "run", "refresh", "--detach"); err != nil { + t.Fatalf("detached execution failed: %v\n%s", err, out) + } deadline := time.Now().Add(15 * time.Second) for s.try(t, "test -f "+base+"/data/started") != nil { if time.Now().After(deadline) { diff --git a/e2e/server_test.go b/e2e/server_test.go index 2aebd2a..f7db594 100644 --- a/e2e/server_test.go +++ b/e2e/server_test.go @@ -257,7 +257,10 @@ ExecStart=/usr/bin/docker compose -p observer -f /var/lib/ob/observer/current/co // An operator run with an input override reaches the container as its // environment, is journaled with the operator, and shows up as such. - manual := s.mustOb(t, dir, "job", "run", "input-chore", "--input", "GREETING=hello", "--output", "json") + if out, err := s.obInput(t, dir, s.obHome(t), "y\n", "job", "run", "input-chore", "--input", "GREETING=hello"); err != nil { + t.Fatalf("operator job run failed: %v\n%s", err, out) + } + manual := s.mustOb(t, dir, "job", "history", "input-chore", "--output", "json") for _, want := range []string{`"GREETING": "hello"`, `"outcome": "success"`, `"trigger": "operator"`} { if !strings.Contains(manual, want) { t.Fatalf("operator run result is missing %q:\n%s", want, manual) @@ -334,7 +337,9 @@ HTTPServer(("127.0.0.1", 18080), Handler).handle_request() // clears the failure from `ob status`; only a later successful run // does. An operator run with the input that makes the job finish in time // is that run, and it must leave status green for the steps after. - s.mustOb(t, dir, "job", "run", "timeout-chore", "--input", "SLEEP=0") + if out, err := s.obInput(t, dir, s.obHome(t), "y\n", "job", "run", "timeout-chore", "--input", "SLEEP=0"); err != nil { + t.Fatalf("operator recovery run failed: %v\n%s", err, out) + } if cleared := s.mustOb(t, dir, "status"); !strings.Contains(cleared, "schedule timeout-chore active") { t.Fatalf("a successful operator run did not clear the recorded timeout:\n%s", cleared) }