| ... | @@ -2850,6 +2850,73 @@ it.layer(layerTest)("ProjectionStoreV2", (it) => { | ... | @@ -2850,6 +2850,73 @@ it.layer(layerTest)("ProjectionStoreV2", (it) => { |
| 2850 | }), | 2850 | }), |
| 2851 | ); | 2851 | ); |
| 2852 | | 2852 | |
| | 2853 | it.effect("selects the latest waiting secret only from active runs", () => |
| | 2854 | Effect.gen(function* () { |
| | 2855 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; |
| | 2856 | const sql = yield* SqlClient.SqlClient; |
| | 2857 | const suffix = "shell-pending-secret"; |
| | 2858 | const threadId = yield* addRolledBackRecoveryCandidate(suffix); |
| | 2859 | const runId = RunId.make(`run:${suffix}:rolled-back`); |
| | 2860 | const nodeId = NodeId.make(`node:${suffix}:rolled-back`); |
| | 2861 | const now = yield* DateTime.now; |
| | 2862 | const addSecret = (id: string, ordinal: number, status: "waiting" | "completed") => |
| | 2863 | projectionStore.apply({ |
| | 2864 | id: EventId.make(`event:${id}`), |
| | 2865 | type: "turn-item.updated", |
| | 2866 | threadId, |
| | 2867 | runId, |
| | 2868 | nodeId, |
| | 2869 | driver, |
| | 2870 | occurredAt: now, |
| | 2871 | payload: { |
| | 2872 | id: TurnItemId.make(id), |
| | 2873 | threadId, |
| | 2874 | runId, |
| | 2875 | nodeId, |
| | 2876 | providerThreadId: null, |
| | 2877 | providerTurnId: null, |
| | 2878 | nativeItemRef: null, |
| | 2879 | parentItemId: null, |
| | 2880 | ordinal, |
| | 2881 | status, |
| | 2882 | title: null, |
| | 2883 | startedAt: now, |
| | 2884 | completedAt: null, |
| | 2885 | updatedAt: now, |
| | 2886 | type: "secret_request", |
| | 2887 | label: "Test credential", |
| | 2888 | reason: "Test pending input", |
| | 2889 | secretStatus: status === "waiting" ? "pending" : "saved", |
| | 2890 | }, |
| | 2891 | }); |
| | 2892 | yield* addSecret("secret:a", 2, "waiting"); |
| | 2893 | yield* addSecret("secret:b", 3, "waiting"); |
| | 2894 | yield* addSecret("secret:c", 4, "completed"); |
| | 2895 | |
| | 2896 | for (const status of [ |
| | 2897 | "preparing", |
| | 2898 | "starting", |
| | 2899 | "running", |
| | 2900 | "waiting", |
| | 2901 | "completed", |
| | 2902 | "rolled_back", |
| | 2903 | ]) { |
| | 2904 | yield* sql` |
| | 2905 | UPDATE orchestration_v2_projection_runs |
| | 2906 | SET status = ${status}, payload_json = json_set(payload_json, '$.status', ${status}) |
| | 2907 | WHERE run_id = ${runId} |
| | 2908 | `; |
| | 2909 | const shell = yield* projectionStore.getShellSnapshot(); |
| | 2910 | const thread = shell.threads.find((candidate) => candidate.id === threadId); |
| | 2911 | assert.isDefined(thread); |
| | 2912 | assert.equal( |
| | 2913 | thread?.pendingRuntimeRequest?.id ?? null, |
| | 2914 | status === "completed" || status === "rolled_back" ? null : "secret:b", |
| | 2915 | ); |
| | 2916 | } |
| | 2917 | }), |
| | 2918 | ); |
| | 2919 | |
| 2853 | it.effect("builds shell snapshots without decoding full turn item payloads", () => | 2920 | it.effect("builds shell snapshots without decoding full turn item payloads", () => |
| 2854 | Effect.gen(function* () { | 2921 | Effect.gen(function* () { |
| 2855 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; | 2922 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; |