| ... | @@ -59,11 +59,11 @@ describe("EventNdjsonLogger", () => { | ... | @@ -59,11 +59,11 @@ describe("EventNdjsonLogger", () => { |
| 59 | const second = parseLogLine(fs.readFileSync(threadTwoPath, "utf8").trim()); | 59 | const second = parseLogLine(fs.readFileSync(threadTwoPath, "utf8").trim()); |
| 60 | | 60 | |
| 61 | assert.equal(Number.isNaN(Date.parse(first.observedAt)), false); | 61 | assert.equal(Number.isNaN(Date.parse(first.observedAt)), false); |
| 62 | assert.equal(first.stream, "NATIVE"); | 62 | assert.equal(first.stream, "NTIVE"); |
| 63 | assert.equal(first.payload, '{"threadId":"provider-thread-1","id":"evt-1"}'); | 63 | assert.equal(first.payload, '{"threadId":"provider-thread-1","id":"evt-1"}'); |
| 64 | | 64 | |
| 65 | assert.equal(Number.isNaN(Date.parse(second.observedAt)), false); | 65 | assert.equal(Number.isNaN(Date.parse(second.observedAt)), false); |
| 66 | assert.equal(second.stream, "NATIVE"); | 66 | assert.equal(second.stream, "NTIVE"); |
| 67 | assert.equal( | 67 | assert.equal( |
| 68 | second.payload, | 68 | second.payload, |
| 69 | '{"type":"turn.completed","threadId":"provider-thread-2","id":"evt-2"}', | 69 | '{"type":"turn.completed","threadId":"provider-thread-2","id":"evt-2"}', |
| ... | @@ -74,40 +74,42 @@ describe("EventNdjsonLogger", () => { | ... | @@ -74,40 +74,42 @@ describe("EventNdjsonLogger", () => { |
| 74 | }), | 74 | }), |
| 75 | ); | 75 | ); |
| 76 | | 76 | |
| 77 | it.effect("falls back to a global segment when orchestration thread id is missing or invalid", () => | 77 | it.effect( |
| 78 | Effect.gen(function* () { | 78 | "falls back to a global segment when orchestration thread id is missing or invalid", |
| 79 | const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); | 79 | () => |
| 80 | const basePath = path.join(tempDir, "provider-canonical.ndjson"); | 80 | Effect.gen(function* () { |
| 81 | | 81 | const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-provider-log-")); |
| 82 | try { | 82 | const basePath = path.join(tempDir, "provider-canonical.ndjson"); |
| 83 | const logger = yield* makeEventNdjsonLogger(basePath, { stream: "orchestration" }); | 83 | |
| 84 | assert.notEqual(logger, undefined); | 84 | try { |
| 85 | if (!logger) { | 85 | const logger = yield* makeEventNdjsonLogger(basePath, { stream: "orchestration" }); |
| 86 | return; | 86 | assert.notEqual(logger, undefined); |
| | 87 | if (!logger) { |
| | 88 | return; |
| | 89 | } |
| | 90 | |
| | 91 | yield* logger.write({ id: "evt-no-thread" }, null); |
| | 92 | yield* logger.write({ id: "evt-invalid-thread" }, "!!!" as unknown as ThreadId); |
| | 93 | yield* logger.close(); |
| | 94 | |
| | 95 | const globalPath = path.join(tempDir, "_global.log"); |
| | 96 | assert.equal(fs.existsSync(globalPath), true); |
| | 97 | const lines = fs |
| | 98 | .readFileSync(globalPath, "utf8") |
| | 99 | .trim() |
| | 100 | .split("\n") |
| | 101 | .map((line) => parseLogLine(line)); |
| | 102 | assert.equal(lines.length, 2); |
| | 103 | assert.equal(Number.isNaN(Date.parse(lines[0]?.observedAt ?? "")), false); |
| | 104 | assert.equal(Number.isNaN(Date.parse(lines[1]?.observedAt ?? "")), false); |
| | 105 | assert.equal(lines[0]?.stream, "CANON"); |
| | 106 | assert.equal(lines[0]?.payload, '{"id":"evt-no-thread"}'); |
| | 107 | assert.equal(lines[1]?.stream, "CANON"); |
| | 108 | assert.equal(lines[1]?.payload, '{"id":"evt-invalid-thread"}'); |
| | 109 | } finally { |
| | 110 | fs.rmSync(tempDir, { recursive: true, force: true }); |
| 87 | } | 111 | } |
| 88 | | 112 | }), |
| 89 | yield* logger.write({ id: "evt-no-thread" }, null); | | |
| 90 | yield* logger.write({ id: "evt-invalid-thread" }, "!!!" as unknown as ThreadId); | | |
| 91 | yield* logger.close(); | | |
| 92 | | | |
| 93 | const globalPath = path.join(tempDir, "_global.log"); | | |
| 94 | assert.equal(fs.existsSync(globalPath), true); | | |
| 95 | const lines = fs | | |
| 96 | .readFileSync(globalPath, "utf8") | | |
| 97 | .trim() | | |
| 98 | .split("\n") | | |
| 99 | .map((line) => parseLogLine(line)); | | |
| 100 | assert.equal(lines.length, 2); | | |
| 101 | assert.equal(Number.isNaN(Date.parse(lines[0]?.observedAt ?? "")), false); | | |
| 102 | assert.equal(Number.isNaN(Date.parse(lines[1]?.observedAt ?? "")), false); | | |
| 103 | assert.equal(lines[0]?.stream, "ORCHESTRATION"); | | |
| 104 | assert.equal(lines[0]?.payload, '{"id":"evt-no-thread"}'); | | |
| 105 | assert.equal(lines[1]?.stream, "ORCHESTRATION"); | | |
| 106 | assert.equal(lines[1]?.payload, '{"id":"evt-invalid-thread"}'); | | |
| 107 | } finally { | | |
| 108 | fs.rmSync(tempDir, { recursive: true, force: true }); | | |
| 109 | } | | |
| 110 | }), | | |
| 111 | ); | 113 | ); |
| 112 | | 114 | |
| 113 | it.effect("rotates per-thread files when max size is exceeded", () => | 115 | it.effect("rotates per-thread files when max size is exceeded", () => |
| ... | @@ -144,12 +146,18 @@ describe("EventNdjsonLogger", () => { | ... | @@ -144,12 +146,18 @@ describe("EventNdjsonLogger", () => { |
| 144 | .filter((entry) => entry === fileStem || entry.startsWith(`${fileStem}.`)) | 146 | .filter((entry) => entry === fileStem || entry.startsWith(`${fileStem}.`)) |
| 145 | .toSorted(); | 147 | .toSorted(); |
| 146 | | 148 | |
| 147 | assert.equal(matchingFiles.some((entry) => entry === `${fileStem}.1`), true); | 149 | assert.equal( |
| | 150 | matchingFiles.some((entry) => entry === `${fileStem}.1`), |
| | 151 | true, |
| | 152 | ); |
| 148 | assert.equal( | 153 | assert.equal( |
| 149 | matchingFiles.some((entry) => entry === fileStem || entry === `${fileStem}.2`), | 154 | matchingFiles.some((entry) => entry === fileStem || entry === `${fileStem}.2`), |
| 150 | true, | 155 | true, |
| 151 | ); | 156 | ); |
| 152 | assert.equal(matchingFiles.some((entry) => entry === `${fileStem}.3`), false); | 157 | assert.equal( |
| | 158 | matchingFiles.some((entry) => entry === `${fileStem}.3`), |
| | 159 | false, |
| | 160 | ); |
| 153 | } finally { | 161 | } finally { |
| 154 | fs.rmSync(tempDir, { recursive: true, force: true }); | 162 | fs.rmSync(tempDir, { recursive: true, force: true }); |
| 155 | } | 163 | } |