alt/services/worker/internal/jobs/runner_test.go
toki bdbfe8228e feat: scheduled market data refresh - parser_map, refresh status model, CLI output, scheduler backfill, socket handlers
- Add parser_map for market data refresh configuration propagation (cli/worker/api)
- Implement refresh status model with SQLite persistence (status_store.go)
- Add scheduler refresh status headless output to CLI operator
- Extend backfill scheduler with status tracking (start/complete/fail)
- Add socket events for scheduler refresh status (start/complete/fail)
- Update proto definitions for refresh status
- Add generated PB files for client
2026-06-22 18:46:45 +09:00

270 lines
6.9 KiB
Go

package jobs
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
)
func TestRunnerDispatchesRegisteredHandler(t *testing.T) {
runner := NewRunner()
called := false
var receivedPayload json.RawMessage
testKind := Kind("test_job")
runner.Register(testKind, func(ctx context.Context, payload json.RawMessage) error {
called = true
receivedPayload = payload
return nil
})
job := Job{
ID: "job-1",
Kind: testKind,
Payload: json.RawMessage(`{"key":"value"}`),
}
err := runner.Execute(context.Background(), job)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !called {
t.Error("expected handler to be called, but it was not")
}
if string(receivedPayload) != `{"key":"value"}` {
t.Errorf("expected payload %q, got %q", `{"key":"value"}`, string(receivedPayload))
}
}
func TestRunnerRejectsUnknownKind(t *testing.T) {
runner := NewRunner()
job := Job{
ID: "job-2",
Kind: Kind("non_existent"),
}
err := runner.Execute(context.Background(), job)
if err == nil {
t.Fatal("expected error for unknown job kind, got nil")
}
}
func TestRunnerRespectsCanceledContext(t *testing.T) {
runner := NewRunner()
testKind := Kind("cancellation_test")
runner.Register(testKind, func(ctx context.Context, payload json.RawMessage) error {
return nil
})
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel context immediately
job := Job{
ID: "job-3",
Kind: testKind,
}
err := runner.Execute(ctx, job)
if err == nil {
t.Fatal("expected error due to canceled context, got nil")
}
if !errors.Is(err, context.Canceled) {
t.Errorf("expected error %v, got %v", context.Canceled, err)
}
}
func TestRunnerHandlesPanic(t *testing.T) {
runner := NewRunner()
panicKind := Kind("panic_job")
runner.Register(panicKind, func(ctx context.Context, payload json.RawMessage) error {
panic("something went critically wrong")
})
job := Job{
ID: "job-4",
Kind: panicKind,
}
err := runner.Execute(context.Background(), job)
if err == nil {
t.Fatal("expected error from panic recovery, got nil")
}
if err.Error() != "job panicked: something went critically wrong" {
t.Errorf("unexpected error message: %v", err)
}
}
func TestRegisterBuiltins(t *testing.T) {
runner := NewRunner()
RegisterBuiltins(runner)
expectedCount := 3
if runner.Len() != expectedCount {
t.Errorf("expected %d registered built-in handlers, got %d", expectedCount, runner.Len())
}
}
// --- ExecuteWithResult tests ---
func TestRunnerExecuteWithResultSuccess(t *testing.T) {
runner := NewRunner()
testKind := Kind("ewr_success")
var payload json.RawMessage
runner.Register(testKind, func(ctx context.Context, p json.RawMessage) error {
payload = p
return nil
})
runner.RegisterWithResult(testKind, func(ctx context.Context, p json.RawMessage) (Result, error) {
return Result{Bars: 42, Instruments: 3}, nil
})
job := Job{
ID: "ewr-1",
Kind: testKind,
Payload: json.RawMessage(`{"key":"value"}`),
}
res, hasResult, err := runner.ExecuteWithResult(context.Background(), job)
if err != nil {
t.Fatalf("ExecuteWithResult: unexpected error: %v", err)
}
if !hasResult {
t.Fatal("ExecuteWithResult: expected hasResult=true, got false")
}
if res.Bars != 42 || res.Instruments != 3 {
t.Errorf("ExecuteWithResult: expected {Bars:42,Instruments:3}, got %+v", res)
}
// Verify the handler was called with the correct payload.
_ = payload // payload is captured by the Register handler; assertion is above.
}
func TestRunnerExecuteWithResultMissingHandler(t *testing.T) {
runner := NewRunner()
testKind := Kind("ewr_missing")
// Only register Handler, not WithResultHandler
runner.Register(testKind, func(ctx context.Context, p json.RawMessage) error {
return nil
})
job := Job{
ID: "ewr-2",
Kind: testKind,
}
res, hasResult, err := runner.ExecuteWithResult(context.Background(), job)
if err != nil {
t.Fatalf("ExecuteWithResult: expected no error for missing handler, got: %v", err)
}
if hasResult {
t.Fatal("ExecuteWithResult: expected hasResult=false for missing handler, got true")
}
if res.Bars != 0 {
t.Errorf("ExecuteWithResult: expected zero result for missing handler, got %+v", res)
}
}
func TestRunnerExecuteWithResultHandlerError(t *testing.T) {
runner := NewRunner()
testKind := Kind("ewr_error")
wantErr := fmt.Errorf("import failed: connection reset")
runner.Register(testKind, func(ctx context.Context, p json.RawMessage) error {
return nil
})
runner.RegisterWithResult(testKind, func(ctx context.Context, p json.RawMessage) (Result, error) {
return Result{}, wantErr
})
job := Job{
ID: "ewr-3",
Kind: testKind,
}
res, hasResult, err := runner.ExecuteWithResult(context.Background(), job)
if err == nil {
t.Fatal("ExecuteWithResult: expected error from handler, got nil")
}
if err != wantErr {
t.Errorf("ExecuteWithResult: expected error %v, got %v", wantErr, err)
}
if hasResult {
t.Fatal("ExecuteWithResult: expected hasResult=false on error, got true")
}
if res.Bars != 0 {
t.Errorf("ExecuteWithResult: expected zero result on error, got %+v", res)
}
}
func TestRunnerExecuteWithResultCanceledContext(t *testing.T) {
runner := NewRunner()
testKind := Kind("ewr_cancel")
runner.Register(testKind, func(ctx context.Context, p json.RawMessage) error {
return nil
})
runner.RegisterWithResult(testKind, func(ctx context.Context, p json.RawMessage) (Result, error) {
return Result{Bars: 10}, nil
})
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
job := Job{
ID: "ewr-4",
Kind: testKind,
}
res, hasResult, err := runner.ExecuteWithResult(ctx, job)
if err == nil {
t.Fatal("ExecuteWithResult: expected context.Canceled error, got nil")
}
if !errors.Is(err, context.Canceled) {
t.Errorf("ExecuteWithResult: expected context.Canceled, got %v", err)
}
if hasResult {
t.Fatal("ExecuteWithResult: expected hasResult=false on cancel, got true")
}
if res.Bars != 0 {
t.Errorf("ExecuteWithResult: expected zero result on cancel, got %+v", res)
}
}
func TestRunnerExecuteWithResultPanic(t *testing.T) {
runner := NewRunner()
testKind := Kind("ewr_panic")
runner.Register(testKind, func(ctx context.Context, p json.RawMessage) error {
return nil
})
runner.RegisterWithResult(testKind, func(ctx context.Context, p json.RawMessage) (Result, error) {
panic("boom in handler")
})
job := Job{
ID: "ewr-5",
Kind: testKind,
}
res, hasResult, err := runner.ExecuteWithResult(context.Background(), job)
if err == nil {
t.Fatal("ExecuteWithResult: expected panic error, got nil")
}
if !strings.Contains(err.Error(), "panicked") {
t.Errorf("ExecuteWithResult: expected 'panicked' in error message, got: %v", err)
}
if hasResult {
t.Fatal("ExecuteWithResult: expected hasResult=false on panic, got true")
}
if res.Bars != 0 {
t.Errorf("ExecuteWithResult: expected zero result on panic, got %+v", res)
}
}