| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "testing" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | func TestEmitConfigLoadWarningsRequiresContextAndOwnsPayload(t *testing.T) { |
| 10 | if (&App{}).emitConfigLoadWarnings(1, []string{"warning"}) { |
| 11 | t.Fatal("handler accepted warnings without a host context") |
| 12 | } |
| 13 | if (&App{ctx: context.Background()}).emitConfigLoadWarnings(1, nil) { |
| 14 | t.Fatal("handler accepted an empty warning list") |
| 15 | } |
| 16 | |
| 17 | type emittedEvent struct { |
| 18 | name string |
| 19 | warnings []string |
| 20 | revision uint64 |
| 21 | } |
| 22 | started := make(chan struct{}) |
| 23 | release := make(chan struct{}) |
| 24 | events := make(chan emittedEvent, 1) |
| 25 | app := &App{ctx: context.Background()} |
| 26 | app.runtimeEvents.emit = func(_ context.Context, name string, payload ...any) { |
| 27 | close(started) |
| 28 | <-release |
| 29 | warnings, _ := payload[0].([]string) |
| 30 | revision, _ := payload[1].(uint64) |
| 31 | events <- emittedEvent{name: name, warnings: warnings, revision: revision} |
| 32 | } |
| 33 | |
| 34 | warnings := []string{"user config is invalid"} |
| 35 | if !app.emitConfigLoadWarnings(42, warnings) { |
| 36 | t.Fatal("handler rejected warnings with a host context") |
| 37 | } |
| 38 | select { |
| 39 | case <-started: |
| 40 | case <-time.After(5 * time.Second): |
| 41 | t.Fatal("runtime event emitter did not start") |
| 42 | } |
| 43 | warnings[0] = "mutated by caller" |
| 44 | close(release) |
| 45 | |
| 46 | select { |
| 47 | case got := <-events: |
| 48 | if got.name != configLoadWarningsEvent { |
| 49 | t.Fatalf("event name = %q, want %q", got.name, configLoadWarningsEvent) |
| 50 | } |
| 51 | if got.revision != 42 { |
| 52 | t.Fatalf("event revision = %d, want 42", got.revision) |
| 53 | } |
| 54 | if len(got.warnings) != 1 || got.warnings[0] != "user config is invalid" { |
| 55 | t.Fatalf("event warnings = %v, want owned original payload", got.warnings) |
| 56 | } |
| 57 | case <-time.After(5 * time.Second): |
| 58 | t.Fatal("runtime event was not delivered") |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | func TestConfigLoadWarningRevisionsFenceEventsStartedBeforeReload(t *testing.T) { |
| 63 | t.Setenv("REASONIX_HOME", t.TempDir()) |
| 64 | app := &App{ctx: context.Background()} |
| 65 | type warningEvent struct { |
| 66 | warnings []string |
| 67 | revision uint64 |
| 68 | } |
| 69 | events := make(chan warningEvent, 2) |
| 70 | app.runtimeEvents.emit = func(_ context.Context, _ string, payload ...any) { |
| 71 | warnings, _ := payload[0].([]string) |
| 72 | revision, _ := payload[1].(uint64) |
| 73 | events <- warningEvent{warnings: warnings, revision: revision} |
| 74 | } |
| 75 | |
| 76 | oldBuildHandler := app.configLoadWarningsHandler() |
| 77 | reloaded, err := app.ReloadUserConfig() |
| 78 | if err != nil { |
| 79 | t.Fatalf("ReloadUserConfig: %v", err) |
| 80 | } |
| 81 | if reloaded.ConfigWarningsRevision == 0 { |
| 82 | t.Fatal("reload did not return a config warning revision") |
| 83 | } |
| 84 | if !oldBuildHandler([]string{"stale warning"}) { |
| 85 | t.Fatal("old build handler rejected warning") |
| 86 | } |
| 87 | newBuildHandler := app.configLoadWarningsHandler() |
| 88 | if !newBuildHandler([]string{"new warning"}) { |
| 89 | t.Fatal("new build handler rejected warning") |
| 90 | } |
| 91 | |
| 92 | receive := func() warningEvent { |
| 93 | t.Helper() |
| 94 | select { |
| 95 | case payload := <-events: |
| 96 | return payload |
| 97 | case <-time.After(5 * time.Second): |
| 98 | t.Fatal("runtime warning event was not delivered") |
| 99 | return warningEvent{} |
| 100 | } |
| 101 | } |
| 102 | oldPayload := receive() |
| 103 | newPayload := receive() |
| 104 | if len(oldPayload.warnings) != 1 || oldPayload.revision >= reloaded.ConfigWarningsRevision { |
| 105 | t.Fatalf("old build event = %+v, reload barrier = %d", oldPayload, reloaded.ConfigWarningsRevision) |
| 106 | } |
| 107 | if len(newPayload.warnings) != 1 || newPayload.revision <= reloaded.ConfigWarningsRevision { |
| 108 | t.Fatalf("new build event = %+v, reload barrier = %d", newPayload, reloaded.ConfigWarningsRevision) |
| 109 | } |
| 110 | } |
| 111 |