From 9661dc7d5eb3e20eb2032b65697d453c1d283a5c Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 8 May 2026 19:34:53 -0600 Subject: [PATCH] remove old compat test code --- __tests__/backwardsCompat/codec.test.ts | 313 ------------------ .../schemaSerialization.test.ts | 260 --------------- package-lock.json | 19 +- package.json | 1 - 4 files changed, 2 insertions(+), 591 deletions(-) delete mode 100644 __tests__/backwardsCompat/codec.test.ts delete mode 100644 __tests__/backwardsCompat/schemaSerialization.test.ts diff --git a/__tests__/backwardsCompat/codec.test.ts b/__tests__/backwardsCompat/codec.test.ts deleted file mode 100644 index 22ef3f3a..00000000 --- a/__tests__/backwardsCompat/codec.test.ts +++ /dev/null @@ -1,313 +0,0 @@ -/** - * Backwards compatibility tests for codec message round-trips. - * - * These tests verify that messages encoded with the legacy TypeBox (0.34.x) - * can be decoded and validated by the new TypeBox (1.0), and vice versa. - * This ensures that during a rolling upgrade, servers/clients using different - * river versions can communicate. - */ -import { describe, test, expect } from 'vitest'; -import { Type as LegacyType } from 'legacyTypebox'; -import { Value as LegacyValue } from 'legacyTypebox/value'; -import { Type as NewType } from 'typebox'; -import { Value as NewValue } from 'typebox/value'; -import { NaiveJsonCodec, BinaryCodec, CodecMessageAdapter } from '../../codec'; -import { - OpaqueTransportMessageSchema, - type OpaqueTransportMessage, -} from '../../transport/message'; -import { Uint8ArrayType } from '../../customSchemas'; - -function makeTransportMessage( - payload: unknown, - overrides: Partial = {}, -): OpaqueTransportMessage { - return { - id: 'msg-1', - from: 'client-1', - to: 'server-1', - seq: 0, - ack: 0, - streamId: 'stream-1', - controlFlags: 0, - payload, - ...overrides, - }; -} - -const LegacyOpaqueTransportMessageSchema = LegacyType.Object({ - id: LegacyType.String(), - from: LegacyType.String(), - to: LegacyType.String(), - seq: LegacyType.Integer(), - ack: LegacyType.Integer(), - serviceName: LegacyType.Optional(LegacyType.String()), - procedureName: LegacyType.Optional(LegacyType.String()), - streamId: LegacyType.String(), - controlFlags: LegacyType.Integer(), - tracing: LegacyType.Optional( - LegacyType.Object({ - traceparent: LegacyType.String(), - tracestate: LegacyType.String(), - }), - ), - payload: LegacyType.Unknown(), -}); - -describe.each([ - { name: 'naive JSON codec', codec: NaiveJsonCodec }, - { name: 'binary codec', codec: BinaryCodec }, -])('codec backwards compatibility ($name)', ({ codec }) => { - const adapter = new CodecMessageAdapter(codec); - - describe('basic message round-trip', () => { - test('message with object payload survives encode/decode', () => { - const msg = makeTransportMessage({ greeting: 'hello', count: 42 }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - expect(decoded.value).toEqual(msg); - }); - - test('message with nested result payload', () => { - const msg = makeTransportMessage({ - ok: true, - payload: { result: 42 }, - }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - expect(decoded.value).toEqual(msg); - }); - - test('message with error payload (Err result format)', () => { - const msg = makeTransportMessage({ - ok: false, - payload: { - code: 'SOME_ERROR', - message: 'something went wrong', - extras: { detail: 'extra info' }, - }, - }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - expect(decoded.value).toEqual(msg); - }); - }); - - describe('Uint8Array payload handling', () => { - test('message with Uint8Array in payload survives round-trip', () => { - const bytes = new Uint8Array([72, 101, 108, 108, 111]); - const msg = makeTransportMessage({ - ok: true, - payload: { contents: bytes }, - }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - const decodedPayload = decoded.value.payload as { - ok: boolean; - payload: { contents: Uint8Array }; - }; - expect(decodedPayload.ok).toBe(true); - expect(new Uint8Array(decodedPayload.payload.contents)).toEqual(bytes); - }); - }); - - describe('cross-version message validation', () => { - test('encoded message passes new schema validation', () => { - const msg = makeTransportMessage({ ok: true, payload: { result: 1 } }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - expect(NewValue.Check(OpaqueTransportMessageSchema, decoded.value)).toBe( - true, - ); - }); - - test('encoded message passes legacy schema validation', () => { - const msg = makeTransportMessage({ ok: true, payload: { result: 1 } }); - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - expect( - LegacyValue.Check(LegacyOpaqueTransportMessageSchema, decoded.value), - ).toBe(true); - }); - }); - - describe('cross-version payload validation', () => { - test('object validated by legacy TypeBox is also valid under new TypeBox', () => { - const legacySchema = LegacyType.Object({ - name: LegacyType.String(), - age: LegacyType.Number(), - }); - const newSchema = NewType.Object({ - name: NewType.String(), - age: NewType.Number(), - }); - - const data = { name: 'Alice', age: 30 }; - expect(LegacyValue.Check(legacySchema, data)).toBe(true); - expect(NewValue.Check(newSchema, data)).toBe(true); - }); - - test('union validated by legacy TypeBox is also valid under new TypeBox', () => { - const legacySchema = LegacyType.Union([ - LegacyType.Object({ - code: LegacyType.Literal('ERR_A'), - message: LegacyType.String(), - }), - LegacyType.Object({ - code: LegacyType.Literal('ERR_B'), - message: LegacyType.String(), - extras: LegacyType.Object({ detail: LegacyType.String() }), - }), - ]); - const newSchema = NewType.Union([ - NewType.Object({ - code: NewType.Literal('ERR_A'), - message: NewType.String(), - }), - NewType.Object({ - code: NewType.Literal('ERR_B'), - message: NewType.String(), - extras: NewType.Object({ detail: NewType.String() }), - }), - ]); - - const data1 = { code: 'ERR_A', message: 'oops' }; - const data2 = { - code: 'ERR_B', - message: 'oops', - extras: { detail: 'info' }, - }; - const invalidData = { code: 'ERR_C', message: 'unknown' }; - - expect(LegacyValue.Check(legacySchema, data1)).toBe(true); - expect(NewValue.Check(newSchema, data1)).toBe(true); - - expect(LegacyValue.Check(legacySchema, data2)).toBe(true); - expect(NewValue.Check(newSchema, data2)).toBe(true); - - expect(LegacyValue.Check(legacySchema, invalidData)).toBe(false); - expect(NewValue.Check(newSchema, invalidData)).toBe(false); - }); - - test('Uint8Array validated by legacy Type.Uint8Array matches new Uint8ArrayType', () => { - const legacySchema = LegacyType.Uint8Array(); - const newSchema = Uint8ArrayType(); - - const validData = new Uint8Array([1, 2, 3]); - expect(LegacyValue.Check(legacySchema, validData)).toBe(true); - expect(NewValue.Check(newSchema, validData)).toBe(true); - - expect(LegacyValue.Check(legacySchema, [1, 2, 3])).toBe(false); - expect(NewValue.Check(newSchema, [1, 2, 3])).toBe(false); - - expect(LegacyValue.Check(legacySchema, 'not bytes')).toBe(false); - expect(NewValue.Check(newSchema, 'not bytes')).toBe(false); - }); - }); - - describe('full transport message round-trip with validation', () => { - test('encode with new TypeBox, validate with legacy', () => { - const msg = makeTransportMessage( - { ok: true, payload: { name: 'test', value: 42 } }, - { - serviceName: 'myService', - procedureName: 'myProcedure', - controlFlags: 1, - }, - ); - - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - expect( - LegacyValue.Check(LegacyOpaqueTransportMessageSchema, decoded.value), - ).toBe(true); - expect(NewValue.Check(OpaqueTransportMessageSchema, decoded.value)).toBe( - true, - ); - }); - - test('handshake request message round-trip', () => { - const msg = makeTransportMessage( - { - type: 'HANDSHAKE_REQ', - protocolVersion: 'v2.0', - sessionId: 'session-1', - expectedSessionState: { - nextExpectedSeq: 0, - nextSentSeq: 0, - }, - }, - { controlFlags: 1 }, - ); - - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - expect(decoded.value).toEqual(msg); - }); - - test('handshake response message round-trip', () => { - const msg = makeTransportMessage( - { - type: 'HANDSHAKE_RESP', - status: { ok: true, sessionId: 'session-123' }, - }, - { controlFlags: 1 }, - ); - - const encoded = adapter.toBuffer(msg); - expect(encoded.ok).toBe(true); - if (!encoded.ok) return; - - const decoded = adapter.fromBuffer(encoded.value); - expect(decoded.ok).toBe(true); - if (!decoded.ok) return; - - expect(decoded.value).toEqual(msg); - }); - }); -}); diff --git a/__tests__/backwardsCompat/schemaSerialization.test.ts b/__tests__/backwardsCompat/schemaSerialization.test.ts deleted file mode 100644 index 039f4b01..00000000 --- a/__tests__/backwardsCompat/schemaSerialization.test.ts +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Backwards compatibility tests for schema serialization. - * - * These tests verify that schemas defined with typebox 1.0 serialize to the - * same JSON Schema output as schemas defined with @sinclair/typebox 0.34.x. - * This is critical because serialized schemas are shared across the wire - * between clients and servers that may be running different versions of river. - */ -import { describe, test, expect } from 'vitest'; -import { Type as LegacyType } from 'legacyTypebox'; -import { Type as NewType } from 'typebox'; -import { Uint8ArrayType } from '../../customSchemas'; - -function strip(schema: object): unknown { - return JSON.parse(JSON.stringify(schema)); -} - -describe('schema serialization backwards compatibility', () => { - describe('primitive types', () => { - test('Type.String()', () => { - expect(strip(NewType.String())).toEqual(strip(LegacyType.String())); - }); - - test('Type.Number()', () => { - expect(strip(NewType.Number())).toEqual(strip(LegacyType.Number())); - }); - - test('Type.Integer()', () => { - expect(strip(NewType.Integer())).toEqual(strip(LegacyType.Integer())); - }); - - test('Type.Boolean()', () => { - expect(strip(NewType.Boolean())).toEqual(strip(LegacyType.Boolean())); - }); - - test('Type.Null()', () => { - expect(strip(NewType.Null())).toEqual(strip(LegacyType.Null())); - }); - - test('Type.Unknown()', () => { - expect(strip(NewType.Unknown())).toEqual(strip(LegacyType.Unknown())); - }); - }); - - describe('literal types', () => { - test('Type.Literal(string)', () => { - expect(strip(NewType.Literal('hello'))).toEqual( - strip(LegacyType.Literal('hello')), - ); - }); - - test('Type.Literal(number)', () => { - expect(strip(NewType.Literal(42))).toEqual(strip(LegacyType.Literal(42))); - }); - - test('Type.Literal(boolean)', () => { - expect(strip(NewType.Literal(true))).toEqual( - strip(LegacyType.Literal(true)), - ); - }); - }); - - describe('composite types', () => { - test('Type.Object with required properties', () => { - const legacy = LegacyType.Object({ - name: LegacyType.String(), - age: LegacyType.Number(), - }); - const current = NewType.Object({ - name: NewType.String(), - age: NewType.Number(), - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Object with optional properties', () => { - const legacy = LegacyType.Object({ - name: LegacyType.String(), - nickname: LegacyType.Optional(LegacyType.String()), - }); - const current = NewType.Object({ - name: NewType.String(), - nickname: NewType.Optional(NewType.String()), - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Object with description', () => { - const legacy = LegacyType.Object( - { a: LegacyType.Number() }, - { description: 'test object' }, - ); - const current = NewType.Object( - { a: NewType.Number() }, - { description: 'test object' }, - ); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Array of primitives', () => { - const legacy = LegacyType.Array(LegacyType.String()); - const current = NewType.Array(NewType.String()); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Array of objects', () => { - const legacy = LegacyType.Array( - LegacyType.Object({ id: LegacyType.Number() }), - ); - const current = NewType.Array(NewType.Object({ id: NewType.Number() })); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Union of objects', () => { - const legacy = LegacyType.Union([ - LegacyType.Object({ code: LegacyType.Literal('A') }), - LegacyType.Object({ code: LegacyType.Literal('B') }), - ]); - const current = NewType.Union([ - NewType.Object({ code: NewType.Literal('A') }), - NewType.Object({ code: NewType.Literal('B') }), - ]); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Type.Union of literals', () => { - const legacy = LegacyType.Union([ - LegacyType.Literal('a'), - LegacyType.Literal('b'), - LegacyType.Literal('c'), - ]); - const current = NewType.Union([ - NewType.Literal('a'), - NewType.Literal('b'), - NewType.Literal('c'), - ]); - expect(strip(current)).toEqual(strip(legacy)); - }); - }); - - describe('Uint8Array custom type', () => { - test('Uint8ArrayType() matches legacy Type.Uint8Array() serialization', () => { - const legacy = LegacyType.Uint8Array(); - const current = Uint8ArrayType(); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Uint8ArrayType with minByteLength matches legacy', () => { - const legacy = LegacyType.Uint8Array({ minByteLength: 1 }); - const current = Uint8ArrayType({ minByteLength: 1 }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Uint8ArrayType with maxByteLength matches legacy', () => { - const legacy = LegacyType.Uint8Array({ maxByteLength: 1024 }); - const current = Uint8ArrayType({ maxByteLength: 1024 }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('Uint8ArrayType with both constraints matches legacy', () => { - const legacy = LegacyType.Uint8Array({ - minByteLength: 1, - maxByteLength: 1024, - }); - const current = Uint8ArrayType({ - minByteLength: 1, - maxByteLength: 1024, - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - }); - - describe('river-specific schema patterns', () => { - test('transport message schema shape', () => { - const legacy = LegacyType.Object({ - id: LegacyType.String(), - from: LegacyType.String(), - to: LegacyType.String(), - seq: LegacyType.Integer(), - ack: LegacyType.Integer(), - serviceName: LegacyType.Optional(LegacyType.String()), - procedureName: LegacyType.Optional(LegacyType.String()), - streamId: LegacyType.String(), - controlFlags: LegacyType.Integer(), - payload: LegacyType.Unknown(), - }); - const current = NewType.Object({ - id: NewType.String(), - from: NewType.String(), - to: NewType.String(), - seq: NewType.Integer(), - ack: NewType.Integer(), - serviceName: NewType.Optional(NewType.String()), - procedureName: NewType.Optional(NewType.String()), - streamId: NewType.String(), - controlFlags: NewType.Integer(), - payload: NewType.Unknown(), - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('result schema shape (Ok/Err)', () => { - const legacy = LegacyType.Union([ - LegacyType.Object({ - ok: LegacyType.Literal(false), - payload: LegacyType.Object({ - code: LegacyType.String(), - message: LegacyType.String(), - extras: LegacyType.Optional(LegacyType.Unknown()), - }), - }), - LegacyType.Object({ - ok: LegacyType.Literal(true), - payload: LegacyType.Unknown(), - }), - ]); - const current = NewType.Union([ - NewType.Object({ - ok: NewType.Literal(false), - payload: NewType.Object({ - code: NewType.String(), - message: NewType.String(), - extras: NewType.Optional(NewType.Unknown()), - }), - }), - NewType.Object({ - ok: NewType.Literal(true), - payload: NewType.Unknown(), - }), - ]); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('error schema with code literal and extras', () => { - const legacy = LegacyType.Object({ - code: LegacyType.Literal('SOME_ERROR'), - message: LegacyType.String(), - extras: LegacyType.Object({ detail: LegacyType.String() }), - }); - const current = NewType.Object({ - code: NewType.Literal('SOME_ERROR'), - message: NewType.String(), - extras: NewType.Object({ detail: NewType.String() }), - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - - test('service schema with Uint8Array field', () => { - const legacy = LegacyType.Object({ - file: LegacyType.String(), - contents: LegacyType.Uint8Array(), - }); - const current = NewType.Object({ - file: NewType.String(), - contents: Uint8ArrayType(), - }); - expect(strip(current)).toEqual(strip(legacy)); - }); - }); -}); diff --git a/package-lock.json b/package-lock.json index dbbe44ce..e8959e39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@replit/river", - "version": "0.217.0", + "version": "0.217.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@replit/river", - "version": "0.217.0", + "version": "0.217.1", "license": "MIT", "dependencies": { "@bufbuild/protobuf": "^2.11.0", @@ -28,7 +28,6 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "legacyTypebox": "npm:@sinclair/typebox@^0.34.49", "prettier": "^3.0.0", "tsup": "^8.4.0", "typebox": "^1.1.38", @@ -3012,14 +3011,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/legacyTypebox": { - "name": "@sinclair/typebox", - "version": "0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", - "dev": true, - "license": "MIT" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6945,12 +6936,6 @@ "json-buffer": "3.0.1" } }, - "legacyTypebox": { - "version": "npm:@sinclair/typebox@0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", - "dev": true - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", diff --git a/package.json b/package.json index 3c73edf1..efdb3414 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "legacyTypebox": "npm:@sinclair/typebox@^0.34.49", "prettier": "^3.0.0", "tsup": "^8.4.0", "typebox": "^1.1.38",