|
1 | 1 | import { parquetReadObjects } from 'hyparquet' |
2 | | -import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 2 | +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' |
3 | 3 | import fs from 'node:fs' |
4 | 4 | import os from 'node:os' |
5 | 5 | import path from 'node:path' |
@@ -108,6 +108,47 @@ describe('uploadPending', () => { |
108 | 108 | ]) |
109 | 109 | }) |
110 | 110 |
|
| 111 | + it('isolates per-job failures so one bad object does not abort the run', async () => { |
| 112 | + writeJsonl('svc-bad', 'logs', yesterday, [ |
| 113 | + { serviceName: 'svc-bad', body: 'x', resource: {}, scope: { attributes: {} }, attributes: {} }, |
| 114 | + ]) |
| 115 | + writeJsonl('svc-good', 'logs', yesterday, [ |
| 116 | + { serviceName: 'svc-good', body: 'y', resource: {}, scope: { attributes: {} }, attributes: {} }, |
| 117 | + ]) |
| 118 | + |
| 119 | + const memory = memoryConnector() |
| 120 | + /** @type {import('../../src/upload/upload.d.ts').StorageConnector} */ |
| 121 | + const connector = { |
| 122 | + scheme: 'flaky', |
| 123 | + async putObject(key, body, contentType) { |
| 124 | + await memory.putObject(key, body, contentType) |
| 125 | + }, |
| 126 | + headObject(key) { |
| 127 | + if (key.includes('svc-bad')) return Promise.reject(new Error('s3 HEAD returned 503')) |
| 128 | + return memory.headObject(key) |
| 129 | + }, |
| 130 | + } |
| 131 | + |
| 132 | + const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) |
| 133 | + const results = await uploadPending( |
| 134 | + { bucket: 'b', prefix: 'collectivus', time: '00:10', signals: ['logs', 'traces', 'metrics'], catchupDays: 7, region: 'us-east-1' }, |
| 135 | + connector, |
| 136 | + outputDir, |
| 137 | + today |
| 138 | + ) |
| 139 | + errSpy.mockRestore() |
| 140 | + |
| 141 | + expect(results).toHaveLength(2) |
| 142 | + const bad = results.find((r) => r.job.service === 'svc-bad') |
| 143 | + const good = results.find((r) => r.job.service === 'svc-good') |
| 144 | + expect(bad?.uploaded).toBe(false) |
| 145 | + expect(bad?.error?.message).toMatch(/503/) |
| 146 | + expect(good?.uploaded).toBe(true) |
| 147 | + expect([...memory.store.keys()]).toEqual([ |
| 148 | + `collectivus/svc-good/logs/date=${yesterday}/data.parquet`, |
| 149 | + ]) |
| 150 | + }) |
| 151 | + |
111 | 152 | it('writes a ledger entry per uploaded file', async () => { |
112 | 153 | writeJsonl('svc-a', 'logs', yesterday, [ |
113 | 154 | { serviceName: 'svc-a', body: 'a', resource: {}, scope: { attributes: {} }, attributes: {} }, |
|
0 commit comments