Skip to content

Commit 6cff2f4

Browse files
Move test code to test helpers (#2813)
1 parent 1f16cdb commit 6cff2f4

File tree

4 files changed

+146
-84
lines changed

4 files changed

+146
-84
lines changed

packages/firestore/src/remote/serializer.ts

+1-81
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ import {
6565
TransformOperation
6666
} from '../model/transform_operation';
6767
import { ExistenceFilter } from './existence_filter';
68-
import { mapCodeFromRpcCode, mapRpcCodeFromCode } from './rpc_error';
68+
import { mapCodeFromRpcCode } from './rpc_error';
6969
import {
7070
DocumentWatchChange,
7171
ExistenceFilterChange,
@@ -417,86 +417,6 @@ export class JsonProtoSerializer {
417417
return fail('invalid batch get response: ' + JSON.stringify(result));
418418
}
419419

420-
private toWatchTargetChangeState(
421-
state: WatchTargetChangeState
422-
): api.TargetChangeTargetChangeType {
423-
switch (state) {
424-
case WatchTargetChangeState.Added:
425-
return 'ADD';
426-
case WatchTargetChangeState.Current:
427-
return 'CURRENT';
428-
case WatchTargetChangeState.NoChange:
429-
return 'NO_CHANGE';
430-
case WatchTargetChangeState.Removed:
431-
return 'REMOVE';
432-
case WatchTargetChangeState.Reset:
433-
return 'RESET';
434-
default:
435-
return fail('Unknown WatchTargetChangeState: ' + state);
436-
}
437-
}
438-
439-
toTestWatchChange(watchChange: WatchChange): api.ListenResponse {
440-
if (watchChange instanceof ExistenceFilterChange) {
441-
return {
442-
filter: {
443-
count: watchChange.existenceFilter.count,
444-
targetId: watchChange.targetId
445-
}
446-
};
447-
}
448-
if (watchChange instanceof DocumentWatchChange) {
449-
if (watchChange.newDoc instanceof Document) {
450-
const doc = watchChange.newDoc;
451-
return {
452-
documentChange: {
453-
document: {
454-
name: this.toName(doc.key),
455-
fields: doc.toProto().mapValue.fields,
456-
updateTime: this.toVersion(doc.version)
457-
},
458-
targetIds: watchChange.updatedTargetIds,
459-
removedTargetIds: watchChange.removedTargetIds
460-
}
461-
};
462-
} else if (watchChange.newDoc instanceof NoDocument) {
463-
const doc = watchChange.newDoc;
464-
return {
465-
documentDelete: {
466-
document: this.toName(doc.key),
467-
readTime: this.toVersion(doc.version),
468-
removedTargetIds: watchChange.removedTargetIds
469-
}
470-
};
471-
} else if (watchChange.newDoc === null) {
472-
return {
473-
documentRemove: {
474-
document: this.toName(watchChange.key),
475-
removedTargetIds: watchChange.removedTargetIds
476-
}
477-
};
478-
}
479-
}
480-
if (watchChange instanceof WatchTargetChange) {
481-
let cause: api.Status | undefined = undefined;
482-
if (watchChange.cause) {
483-
cause = {
484-
code: mapRpcCodeFromCode(watchChange.cause.code),
485-
message: watchChange.cause.message
486-
};
487-
}
488-
return {
489-
targetChange: {
490-
targetChangeType: this.toWatchTargetChangeState(watchChange.state),
491-
targetIds: watchChange.targetIds,
492-
resumeToken: this.toBytes(watchChange.resumeToken),
493-
cause
494-
}
495-
};
496-
}
497-
return fail('Unrecognized watch change: ' + JSON.stringify(watchChange));
498-
}
499-
500420
fromWatchChange(change: api.ListenResponse): WatchChange {
501421
let watchChange: WatchChange;
502422
if ('targetChange' in change) {

packages/firestore/test/unit/specs/spec_test_runner.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,10 @@ import {
102102
setMutation,
103103
TestSnapshotVersion,
104104
version,
105-
byteStringFromString
105+
byteStringFromString,
106+
stringFromBase64String
106107
} from '../../util/helpers';
108+
import { encodeWatchChange } from '../../util/spec_test_helpers';
107109
import { SharedFakeWebStorage, TestPlatform } from '../../util/test_platform';
108110
import {
109111
clearTestPersistence,
@@ -858,7 +860,7 @@ abstract class TestRunner {
858860
}
859861

860862
private async doWatchEvent(watchChange: WatchChange): Promise<void> {
861-
const protoJSON = this.serializer.toTestWatchChange(watchChange);
863+
const protoJSON = encodeWatchChange(watchChange);
862864
this.connection.watchStream!.callOnMessage(protoJSON);
863865

864866
// Put a no-op in the queue so that we know when any outstanding RemoteStore
@@ -1197,7 +1199,13 @@ abstract class TestRunner {
11971199
expect(actualTarget.query).to.deep.equal(expectedTarget.query);
11981200
expect(actualTarget.targetId).to.equal(expectedTarget.targetId);
11991201
expect(actualTarget.readTime).to.equal(expectedTarget.readTime);
1200-
expect(actualTarget.resumeToken).to.equal(expectedTarget.resumeToken);
1202+
expect(actualTarget.resumeToken).to.equal(
1203+
expectedTarget.resumeToken,
1204+
`ResumeToken does not match - expected:
1205+
${stringFromBase64String(
1206+
expectedTarget.resumeToken
1207+
)}, actual: ${stringFromBase64String(expectedTarget.resumeToken)}`
1208+
);
12011209
delete actualTargets[targetId];
12021210
});
12031211
expect(obj.size(actualTargets)).to.equal(

packages/firestore/test/util/helpers.ts

+18
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,24 @@ export function byteStringFromString(value: string): ByteString {
512512
return ByteString.fromBase64String(base64);
513513
}
514514

515+
/**
516+
* Decodes a base 64 decoded string.
517+
*
518+
* Note that this is typed to accept Uint8Arrays to match the types used
519+
* by the spec tests. Since the spec tests only use JSON strings, this method
520+
* throws if an Uint8Array is passed.
521+
*/
522+
export function stringFromBase64String(
523+
value?: string | Uint8Array
524+
): ByteString {
525+
assert(
526+
value === undefined || typeof value === 'string',
527+
'Can only decode base64 encoded strings'
528+
);
529+
const base64 = PlatformSupport.getPlatform().btoa(value ?? '');
530+
return ByteString.fromBase64String(base64);
531+
}
532+
515533
/** Creates a resume token to match the given snapshot version. */
516534
export function resumeTokenForSnapshot(
517535
snapshotVersion: SnapshotVersion
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import {
19+
DocumentWatchChange,
20+
ExistenceFilterChange,
21+
WatchChange,
22+
WatchTargetChange,
23+
WatchTargetChangeState
24+
} from '../../src/remote/watch_change';
25+
import * as api from '../../src/protos/firestore_proto_api';
26+
import { Document, NoDocument } from '../../src/model/document';
27+
import { mapRpcCodeFromCode } from '../../src/remote/rpc_error';
28+
import { fail } from '../../src/util/assert';
29+
import { JsonProtoSerializer } from '../../src/remote/serializer';
30+
import { dbId } from './helpers';
31+
32+
const serializer = new JsonProtoSerializer(dbId('project'), {
33+
useProto3Json: true
34+
});
35+
36+
export function encodeWatchChange(
37+
watchChange: WatchChange
38+
): api.ListenResponse {
39+
if (watchChange instanceof ExistenceFilterChange) {
40+
return {
41+
filter: {
42+
count: watchChange.existenceFilter.count,
43+
targetId: watchChange.targetId
44+
}
45+
};
46+
}
47+
if (watchChange instanceof DocumentWatchChange) {
48+
if (watchChange.newDoc instanceof Document) {
49+
const doc = watchChange.newDoc;
50+
return {
51+
documentChange: {
52+
document: {
53+
name: serializer.toName(doc.key),
54+
fields: doc.toProto().mapValue.fields,
55+
updateTime: serializer.toVersion(doc.version)
56+
},
57+
targetIds: watchChange.updatedTargetIds,
58+
removedTargetIds: watchChange.removedTargetIds
59+
}
60+
};
61+
} else if (watchChange.newDoc instanceof NoDocument) {
62+
const doc = watchChange.newDoc;
63+
return {
64+
documentDelete: {
65+
document: serializer.toName(doc.key),
66+
readTime: serializer.toVersion(doc.version),
67+
removedTargetIds: watchChange.removedTargetIds
68+
}
69+
};
70+
} else if (watchChange.newDoc === null) {
71+
return {
72+
documentRemove: {
73+
document: serializer.toName(watchChange.key),
74+
removedTargetIds: watchChange.removedTargetIds
75+
}
76+
};
77+
}
78+
}
79+
if (watchChange instanceof WatchTargetChange) {
80+
let cause: api.Status | undefined = undefined;
81+
if (watchChange.cause) {
82+
cause = {
83+
code: mapRpcCodeFromCode(watchChange.cause.code),
84+
message: watchChange.cause.message
85+
};
86+
}
87+
return {
88+
targetChange: {
89+
targetChangeType: encodeTargetChangeTargetChangeType(watchChange.state),
90+
targetIds: watchChange.targetIds,
91+
resumeToken: serializer.toBytes(watchChange.resumeToken),
92+
cause
93+
}
94+
};
95+
}
96+
return fail('Unrecognized watch change: ' + JSON.stringify(watchChange));
97+
}
98+
99+
function encodeTargetChangeTargetChangeType(
100+
state: WatchTargetChangeState
101+
): api.TargetChangeTargetChangeType {
102+
switch (state) {
103+
case WatchTargetChangeState.Added:
104+
return 'ADD';
105+
case WatchTargetChangeState.Current:
106+
return 'CURRENT';
107+
case WatchTargetChangeState.NoChange:
108+
return 'NO_CHANGE';
109+
case WatchTargetChangeState.Removed:
110+
return 'REMOVE';
111+
case WatchTargetChangeState.Reset:
112+
return 'RESET';
113+
default:
114+
return fail('Unknown WatchTargetChangeState: ' + state);
115+
}
116+
}

0 commit comments

Comments
 (0)