From 24fddb00f16f3e883cc6a7e5afe4675cf4703dd9 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:04:40 -0500 Subject: [PATCH] add more tests --- dev/src/order.ts | 8 +- dev/src/path.ts | 3 +- dev/system-test/firestore.ts | 174 ++++++++++++++++++++++++++++++----- 3 files changed, 157 insertions(+), 28 deletions(-) diff --git a/dev/src/order.ts b/dev/src/order.ts index c92181209..e0ca4399f 100644 --- a/dev/src/order.ts +++ b/dev/src/order.ts @@ -214,7 +214,7 @@ function compareObjects(left: ApiMapValue, right: ApiMapValue): number { leftKeys.sort(); rightKeys.sort(); for (let i = 0; i < leftKeys.length && i < rightKeys.length; i++) { - const keyComparison = primitiveComparator(leftKeys[i], rightKeys[i]); + const keyComparison = compareUtf8Strings(leftKeys[i], rightKeys[i]); if (keyComparison !== 0) { return keyComparison; } @@ -252,10 +252,10 @@ function stringToUTF8Bytes(str: string): Uint8Array { return new TextEncoder().encode(str); } -function compareUTF8bytes(left: string, right: string): number { +export function compareUtf8Strings(left: string, right: string): number { const leftBytes = stringToUTF8Bytes(left); const rightBytes = stringToUTF8Bytes(right); - return Buffer.compare(Buffer.from(leftBytes), Buffer.from(rightBytes)); + return compareBlobs(Buffer.from(leftBytes), Buffer.from(rightBytes)); } /*! @@ -279,7 +279,7 @@ export function compare(left: api.IValue, right: api.IValue): number { case TypeOrder.BOOLEAN: return primitiveComparator(left.booleanValue!, right.booleanValue!); case TypeOrder.STRING: - return compareUTF8bytes(left.stringValue!, right.stringValue!); + return compareUtf8Strings(left.stringValue!, right.stringValue!); case TypeOrder.NUMBER: return compareNumberProtos(left, right); case TypeOrder.TIMESTAMP: diff --git a/dev/src/path.ts b/dev/src/path.ts index bb16e122d..5410744e7 100644 --- a/dev/src/path.ts +++ b/dev/src/path.ts @@ -17,6 +17,7 @@ import * as firestore from '@google-cloud/firestore'; import {google} from '../protos/firestore_v1_proto_api'; +import {compareUtf8Strings} from './order'; import {isObject} from './util'; import { @@ -191,7 +192,7 @@ abstract class Path { ); } else { // both non-numeric - return this.compareStrings(lhs, rhs); + return compareUtf8Strings(lhs, rhs); } } diff --git a/dev/system-test/firestore.ts b/dev/system-test/firestore.ts index 8aa85cde6..37d111d52 100644 --- a/dev/system-test/firestore.ts +++ b/dev/system-test/firestore.ts @@ -4085,34 +4085,162 @@ describe('Query class', () => { }); }); - it('snapshot listener sorts query by unicode strings same way as server', async () => { - const collection = await testCollectionWithDocs({ - a: {value: 'Łukasiewicz'}, - b: {value: 'Sierpiński'}, - c: {value: '岩澤'}, - d: {value: '🄟'}, - e: {value: 'P'}, - f: {value: '︒'}, - g: {value: '🐵'}, - }); + describe('sort unicode strings', () => { + it('snapshot listener sorts query by unicode strings same as server', async () => { + const collection = await testCollectionWithDocs({ + a: {value: 'Łukasiewicz'}, + b: {value: 'Sierpiński'}, + c: {value: '岩澤'}, + d: {value: '🄟'}, + e: {value: 'P'}, + f: {value: '︒'}, + g: {value: '🐵'}, + }); - const query = collection.orderBy("value"); - const expectedDocs = ['b', 'a', 'c', 'f', 'e', 'd', 'g']; + const query = collection.orderBy('value'); + const expectedDocs = ['b', 'a', 'c', 'f', 'e', 'd', 'g']; - const getSnapshot = await query.get(); - expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); + const getSnapshot = await query.get(); + expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); - const unsubscribe = query.onSnapshot(snapshot => - currentDeferred.resolve(snapshot) - ); + const unsubscribe = query.onSnapshot(snapshot => + currentDeferred.resolve(snapshot) + ); - const watchSnapshot = await waitForSnapshot(); - // Compare the snapshot (including sort order) of a snapshot - snapshotsEqual(watchSnapshot, { - docs: getSnapshot.docs, - docChanges: getSnapshot.docChanges(), + const watchSnapshot = await waitForSnapshot(); + // Compare the snapshot (including sort order) of a snapshot + snapshotsEqual(watchSnapshot, { + docs: getSnapshot.docs, + docChanges: getSnapshot.docChanges(), + }); + unsubscribe(); + }); + + it('snapshot listener sorts query by unicode strings in array same as server', async () => { + const collection = await testCollectionWithDocs({ + a: {value: ['Łukasiewicz']}, + b: {value: ['Sierpiński']}, + c: {value: ['岩澤']}, + d: {value: ['🄟']}, + e: {value: ['P']}, + f: {value: ['︒']}, + g: {value: ['🐵']}, + }); + + const query = collection.orderBy('value'); + const expectedDocs = ['b', 'a', 'c', 'f', 'e', 'd', 'g']; + + const getSnapshot = await query.get(); + expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); + + const unsubscribe = query.onSnapshot(snapshot => + currentDeferred.resolve(snapshot) + ); + + const watchSnapshot = await waitForSnapshot(); + snapshotsEqual(watchSnapshot, { + docs: getSnapshot.docs, + docChanges: getSnapshot.docChanges(), + }); + unsubscribe(); + }); + + it('snapshot listener sorts query by unicode strings in map same as server', async () => { + const collection = await testCollectionWithDocs({ + a: {value: {foo: 'Łukasiewicz'}}, + b: {value: {foo: 'Sierpiński'}}, + c: {value: {foo: '岩澤'}}, + d: {value: {foo: '🄟'}}, + e: {value: {foo: 'P'}}, + f: {value: {foo: '︒'}}, + g: {value: {foo: '🐵'}}, + }); + + const query = collection.orderBy('value'); + const expectedDocs = ['b', 'a', 'c', 'f', 'e', 'd', 'g']; + + const getSnapshot = await query.get(); + expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); + + const unsubscribe = query.onSnapshot(snapshot => + currentDeferred.resolve(snapshot) + ); + + const watchSnapshot = await waitForSnapshot(); + // Compare the snapshot (including sort order) of a snapshot + snapshotsEqual(watchSnapshot, { + docs: getSnapshot.docs, + docChanges: getSnapshot.docChanges(), + }); + unsubscribe(); + }); + it('snapshot listener sorts query by unicode strings in map key same as server', async () => { + const collection = await testCollectionWithDocs({ + a: {value: {Łukasiewicz: true}}, + b: {value: {Sierpiński: true}}, + c: {value: {岩澤: true}}, + d: {value: {'🄟': true}}, + e: {value: {P: true}}, + f: {value: {'︒': true}}, + g: {value: {'🐵': true}}, + }); + + const query = collection.orderBy('value'); + const expectedDocs = ['b', 'a', 'c', 'f', 'e', 'd', 'g']; + + const getSnapshot = await query.get(); + expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); + + const unsubscribe = query.onSnapshot(snapshot => + currentDeferred.resolve(snapshot) + ); + + const watchSnapshot = await waitForSnapshot(); + // Compare the snapshot (including sort order) of a snapshot + snapshotsEqual(watchSnapshot, { + docs: getSnapshot.docs, + docChanges: getSnapshot.docChanges(), + }); + unsubscribe(); + }); + + it('snapshot listener sorts query by unicode strings in document key same as server', async () => { + const collection = await testCollectionWithDocs({ + Łukasiewicz: {value: true}, + Sierpiński: {value: true}, + 岩澤: {value: true}, + '🄟': {value: true}, + P: {value: true}, + '︒': {value: true}, + '🐵': {value: true}, + }); + + const query = collection.orderBy('value'); + const expectedDocs = [ + 'Sierpiński', + 'Łukasiewicz', + '岩澤', + '︒', + 'P', + '🄟', + '🐵', + ]; + + const getSnapshot = await query.get(); + expect(getSnapshot.docs.map(d => d.id)).to.deep.equal(expectedDocs); + + const unsubscribe = query.onSnapshot(snapshot => + currentDeferred.resolve(snapshot) + ); + + const watchSnapshot = await waitForSnapshot(); + // Compare the snapshot (including sort order) of a snapshot + snapshotsEqual(watchSnapshot, { + docs: getSnapshot.docs, + docChanges: getSnapshot.docChanges(), + }); + unsubscribe(); }); - unsubscribe(); }); });