mirror of
https://github.com/tiennm99/rplace.git
synced 2026-10-15 08:17:16 +00:00
refactor(canvas): drop Upstash entirely after successful DO migration
Production migration ran cleanly (samples_checked: 9, mismatches: []), canvas data preserved, drawing verified by user. Phase 4 cleanup: Removed code: - src/lib/canvas-storage.js (Upstash GETRANGE reader) - src/lib/redis-client.js (Upstash REST helpers) - src/lib/rate-limiter.js (orphan since Phase 2) - src/admin/migrate-from-upstash.js (one-shot importer) - src/durable-objects/canvas-room.js #handleImport route - src/durable-objects/lib/chunk-storage.js importFullCanvas helper - src/worker.js /admin/migrate-from-upstash mount + import Removed tests: - test/lib/canvas-storage.test.js - test/lib/redis-client.test.js - test/integration/redis-canvas-roundtrip.test.js - test/durable-objects/canvas-room.test.js (was skipped pending rewrite) - vitest.integration.config.js (only Redis testcontainers used it) Removed deps: - @upstash/redis, ioredis, testcontainers (-184 packages) Removed constants: - REDIS_KEY_PREFIX, REDIS_CANVAS_KEY (only used by deleted code) Removed package.json scripts: test:integration, test:all Removed CF Worker secrets in production: UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN, MIGRATION_TOKEN Tests: 94/94 pass (down from 112 — 18 deleted Upstash-specific). Bundle: 71.90 KiB (down from 79.43 KiB). Production verified: canvas data intact (38% non-zero bytes), migration endpoint returns 404, secret list empty.
This commit is contained in:
1 parent
b890dfb3b7
commit
a977adc62d
18 files changed
+18
-3082
No files matched your search
@@ -1,105 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { CanvasRoom } from '../../src/durable-objects/canvas-room.js';
|
||||
|
||||
// TODO: Phase 4 of canvas-on-do storage plan rewrites these tests against the
|
||||
// new SQLite-backed DO via @cloudflare/vitest-pool-workers. The pre-migration
|
||||
// mocks here can't model state.storage.sql, so the suite is skipped until the
|
||||
// rewrite. See plans/260509-2309-canvas-on-do-storage/phase-04-cleanup-dependency-removal.md.
|
||||
const describeOrSkip = describe.skip;
|
||||
|
||||
/** Create a mock WebSocket */
|
||||
function mockWebSocket() {
|
||||
return { send: vi.fn(), close: vi.fn() };
|
||||
}
|
||||
|
||||
/** Create a mock Durable Object state with Hibernation API */
|
||||
function mockState() {
|
||||
const sockets = new Set();
|
||||
return {
|
||||
acceptWebSocket: vi.fn((ws) => sockets.add(ws)),
|
||||
getWebSockets: vi.fn(() => [...sockets]),
|
||||
_sockets: sockets,
|
||||
};
|
||||
}
|
||||
|
||||
describeOrSkip('CanvasRoom', () => {
|
||||
let state;
|
||||
let room;
|
||||
|
||||
beforeEach(() => {
|
||||
state = mockState();
|
||||
room = new CanvasRoom(state);
|
||||
});
|
||||
|
||||
describe('broadcast', () => {
|
||||
it('sends pixel data to all connected WebSockets', async () => {
|
||||
const ws1 = mockWebSocket();
|
||||
const ws2 = mockWebSocket();
|
||||
state._sockets.add(ws1);
|
||||
state._sockets.add(ws2);
|
||||
|
||||
const pixels = [{ x: 10, y: 20, color: 5 }];
|
||||
const req = new Request('http://internal/broadcast', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(pixels),
|
||||
});
|
||||
|
||||
const res = await room.fetch(req);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const expected = JSON.stringify({ type: 'pixels', pixels });
|
||||
expect(ws1.send).toHaveBeenCalledWith(expected);
|
||||
expect(ws2.send).toHaveBeenCalledWith(expected);
|
||||
});
|
||||
|
||||
it('closes WebSocket on send failure', async () => {
|
||||
const ws = mockWebSocket();
|
||||
ws.send.mockImplementation(() => { throw new Error('disconnected'); });
|
||||
state._sockets.add(ws);
|
||||
|
||||
const req = new Request('http://internal/broadcast', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([{ x: 0, y: 0, color: 1 }]),
|
||||
});
|
||||
|
||||
const res = await room.fetch(req);
|
||||
expect(res.status).toBe(200);
|
||||
expect(ws.close).toHaveBeenCalledWith(1011, 'send failed');
|
||||
});
|
||||
|
||||
it('broadcasts to empty room without error', async () => {
|
||||
const req = new Request('http://internal/broadcast', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([]),
|
||||
});
|
||||
|
||||
const res = await room.fetch(req);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webSocketClose', () => {
|
||||
it('closes the WebSocket with given code and reason', () => {
|
||||
const ws = mockWebSocket();
|
||||
room.webSocketClose(ws, 1000, 'normal', true);
|
||||
expect(ws.close).toHaveBeenCalledWith(1000, 'normal');
|
||||
});
|
||||
});
|
||||
|
||||
describe('webSocketError', () => {
|
||||
it('closes the WebSocket with error code', () => {
|
||||
const ws = mockWebSocket();
|
||||
room.webSocketError(ws, new Error('test'));
|
||||
expect(ws.close).toHaveBeenCalledWith(1011, 'error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('webSocketMessage', () => {
|
||||
it('ignores messages (no-op)', () => {
|
||||
const ws = mockWebSocket();
|
||||
// Should not throw
|
||||
room.webSocketMessage(ws, 'hello');
|
||||
expect(ws.send).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user