public void GetValue_CloneTimestamp() { var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2", Fields = { { "Timestamp", new Value { TimestampValue = CreateProtoTimestamp(1, 30) } }, } }; var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var snapshot = DocumentSnapshot.ForDocument(db, proto, readTime); // If we ask for the field twice, the results should be independent. (We clone the value.) var fieldValue1 = snapshot.GetValue <wkt::Timestamp>("Timestamp"); var fieldValue2 = snapshot.GetValue <wkt::Timestamp>("Timestamp"); Assert.NotSame(fieldValue1, fieldValue2); Assert.Equal(fieldValue1, fieldValue2); fieldValue1.Nanos = 999; Assert.NotEqual(fieldValue1, fieldValue2); }
public void ConvenienceMembers() { var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var query = db.Collection("col1"); var readTime = new Timestamp(10, 2); var doc1 = DocumentSnapshot.ForMissingDocument(db, "projects/proj/databases/db/documents/col1/doc1", readTime); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc2" }; var doc2 = DocumentSnapshot.ForDocument(db, proto, readTime); var docs = new[] { doc1, doc2 }; var querySnapshot = new QuerySnapshot(query, docs, readTime); Assert.Equal(2, querySnapshot.Count); // Indexer Assert.Same(doc1, querySnapshot[0]); Assert.Same(doc2, querySnapshot[1]); Assert.Throws <ArgumentOutOfRangeException>(() => querySnapshot[-1]); Assert.Throws <ArgumentOutOfRangeException>(() => querySnapshot[2]); // Use IEnumerable<DocumentSnapshot> Assert.Equal(docs, querySnapshot); // OfType forces the use of non-generic IEnumerable Assert.Equal(docs, querySnapshot.OfType <DocumentSnapshot>()); }
public void GetValue_CloneLatLng() { var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2", Fields = { { "GeoPoint", new Value { GeoPointValue = new Type.LatLng{ Latitude = 2.5, Longitude = 3.75 } } } } }; var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var snapshot = DocumentSnapshot.ForDocument(db, proto, readTime); // If we ask for the field twice, the results should be independent. (We clone the value.) var fieldValue1 = snapshot.GetValue <Type.LatLng>("GeoPoint"); var fieldValue2 = snapshot.GetValue <Type.LatLng>("GeoPoint"); Assert.NotSame(fieldValue1, fieldValue2); Assert.Equal(fieldValue1, fieldValue2); fieldValue1.Longitude = 4.5; Assert.NotEqual(fieldValue1, fieldValue2); }
public void Equality() { var control = GetSampleSnapshot(); var equalDb = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); // Equal (but distinct) everything var doc1 = control.Document.Clone(); var equal1 = DocumentSnapshot.ForDocument(equalDb, doc1, control.ReadTime); var doc2 = control.Document.Clone(); doc2.CreateTime = null; doc2.UpdateTime = null; // Different create/update times var equal2 = DocumentSnapshot.ForDocument(equalDb, doc2, control.ReadTime); // Different read time var equal3 = DocumentSnapshot.ForDocument(equalDb, doc2, new Timestamp(10, 3)); // Different fields var doc4 = doc1.Clone(); doc4.Fields["other"] = new Value { StringValue = "different" }; var unequal1 = DocumentSnapshot.ForDocument(equalDb, doc4, control.ReadTime); // Different name var doc5 = doc1.Clone(); doc5.Name += "x"; var unequal2 = DocumentSnapshot.ForDocument(equalDb, doc5, control.ReadTime); EqualityTester.AssertEqual(control, new[] { equal1, equal2, equal3 }, new[] { unequal1, unequal2 }); }
private static DocumentSnapshot CreateSnapshot(string docId, int value1, int value2) { var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = s_db.Document($"col/{docId}").Path, Fields = { { "value1", CreateValue(value1) }, { "value2", CreateValue(value2) } } }; return(DocumentSnapshot.ForDocument(s_db, proto, readTime)); }
private static DocumentSnapshot GetSampleSnapshot(string docId) { var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = $"projects/proj/databases/db/documents/col1/{docId}", Fields = { ValueSerializer.SerializeMap(new { Name = docId }) } }; return(DocumentSnapshot.ForDocument(s_db, proto, readTime)); }
internal static DocumentSnapshot GetSampleSnapshot(FirestoreDb db, string docId) { var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = db.Document($"col1/{docId}").Path, Fields = { ValueSerializer.SerializeMap(db.SerializationContext, new { Name = docId }) } }; return(DocumentSnapshot.ForDocument(db, proto, readTime)); }
private DocumentSnapshot CreateSnapshot(string path, int value = 0) { var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = s_db.Document(path).Path, Fields = { { "field", CreateValue(value) } } }; return(DocumentSnapshot.ForDocument(s_db, proto, readTime)); }
public void Equality_Missing() { var nonMissing = GetSampleSnapshot(); var db = nonMissing.Database; var doc = nonMissing.Document; var control = DocumentSnapshot.ForMissingDocument(db, doc.Name, new Timestamp(1, 2)); var equal = DocumentSnapshot.ForMissingDocument(db, doc.Name, new Timestamp(1, 3)); var unequal1 = DocumentSnapshot.ForMissingDocument(db, doc.Name + "x", new Timestamp(1, 2)); var unequal2 = DocumentSnapshot.ForDocument(db, doc, new Timestamp(1, 2)); EqualityTester.AssertEqual(control, new[] { equal }, new[] { unequal1, unequal2 }); }
private static DocumentSnapshot GetSampleSnapshot(string docId) { var db = FirestoreDb.Create("project", "database", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = new Timestamp(1, 10).ToProto(), UpdateTime = new Timestamp(2, 20).ToProto(), Name = $"projects/proj/databases/db/documents/col1/{docId}", Fields = { ValueSerializer.SerializeMap(new { Name = docId }) } }; return(DocumentSnapshot.ForDocument(db, proto, readTime)); }
public void CollectionGroup_InvalidCursor() { var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var collection = s_db.Collection("col1"); var document = new Document { CreateTime = CreateProtoTimestamp(0, 0), UpdateTime = CreateProtoTimestamp(0, 0), Name = collection.Document("doc").Path, Fields = { { "field", CreateArray(CreateValue(1), CreateValue(2)) } } }; var snapshot = DocumentSnapshot.ForDocument(s_db, document, Timestamp.FromProto(document.CreateTime)); // Collection group query for a different collection var query = db.CollectionGroup("col2"); Assert.Throws <ArgumentException>(() => query.StartAt(snapshot)); }
public void ConvertTo_ValueType() { var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2", Fields = { ["Name"] = ProtoHelpers.CreateValue("text"), ["Value"] = ProtoHelpers.CreateValue(100) } }; var document = DocumentSnapshot.ForDocument(db, proto, readTime); var value = document.ConvertTo <SerializationTestData.CustomValueType>(); Assert.Equal("text", value.Name); Assert.Equal(100, value.Value); }
public void ArrayContainsIsEquality() { var collection = s_db.Collection("col"); var document = new Document { CreateTime = CreateProtoTimestamp(0, 0), UpdateTime = CreateProtoTimestamp(0, 0), Name = collection.Document("doc").Path, Fields = { { "field", CreateArray(CreateValue(1), CreateValue(2)) } } }; var snapshot = DocumentSnapshot.ForDocument(s_db, document, Timestamp.FromProto(document.CreateTime)); // An inequality filter would create an implicit ordering here, but "array contains" // is effectively an equality filter, so we should end up with document ID ordering instead. var query = s_db.Collection("col").WhereArrayContains("field", 1).StartAt(snapshot); var structured = query.ToStructuredQuery(); var documentIdOrder = new Order { Direction = Direction.Ascending, Field = FieldPath.DocumentId.ToFieldReference() }; Assert.Equal(new[] { documentIdOrder }, structured.OrderBy); }
private static DocumentSnapshot GetSampleSnapshot() { var poco = new SampleData { Name = "Test", Nested = new NestedData { Score = 20 } }; var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2", Fields = { ValueSerializer.SerializeMap(poco) } }; return(DocumentSnapshot.ForDocument(db, proto, readTime)); }
public void ExistingDocument() { var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2" }; var document = DocumentSnapshot.ForDocument(db, proto, readTime); Assert.Equal(db.Document("col1/doc1/col2/doc2"), document.Reference); Assert.Equal("doc2", document.Id); Assert.Same(db, document.Database); Assert.Same(proto, document.Document); Assert.Equal(new Timestamp(1, 10), document.CreateTime); Assert.Equal(new Timestamp(2, 20), document.UpdateTime); Assert.Equal(readTime, document.ReadTime); Assert.True(document.Exists); // Tests for data access are performed separately. }
public void ConvertTo_WithId() { var db = FirestoreDb.Create("proj", "db", new FakeFirestoreClient()); var readTime = new Timestamp(10, 2); var proto = new Document { CreateTime = CreateProtoTimestamp(1, 10), UpdateTime = CreateProtoTimestamp(2, 20), Name = "projects/proj/databases/db/documents/col1/doc1/col2/doc2", Fields = { ["Name"] = ProtoHelpers.CreateValue("text"), ["Value"] = ProtoHelpers.CreateValue(100) } }; var document = DocumentSnapshot.ForDocument(db, proto, readTime); var converted = document.ConvertTo <SampleDataWithDocumentId>(); Assert.Equal("text", converted.Name); Assert.Equal(100, converted.Value); Assert.Equal("doc2", converted.DocumentId); }
public async Task Listen(SerializableTest wrapper) { ListenTest test = wrapper.Test.Listen; var db = FirestoreDb.Create(ProjectId, DatabaseId, new FakeFirestoreClient()); var query = db.Collection("C").OrderBy("a"); Func <Task> action = async() => { List <QuerySnapshot> snapshots = new List <QuerySnapshot>(); var watchState = new WatchState(query, (snapshot, token) => { snapshots.Add(snapshot); return(Task.FromResult(1)); }); watchState.OnStreamInitialization(StreamInitializationCause.WatchStarting); foreach (var response in test.Responses) { // Fix up the test response to use our watch target ID. ReplaceWatchTargetId(response.TargetChange?.TargetIds); ReplaceWatchTargetId(response.DocumentChange?.TargetIds); ReplaceWatchTargetId(response.DocumentChange?.RemovedTargetIds); ReplaceWatchTargetId(response.DocumentDelete?.RemovedTargetIds); ReplaceWatchTargetId(response.DocumentRemove?.RemovedTargetIds); var result = await watchState.HandleResponseAsync(response, default); if (result == WatchResponseResult.ResetStream) { watchState.OnStreamInitialization(StreamInitializationCause.ResetRequested); } } var expectedSnapshots = test.Snapshots.Select(snapshot => ConvertSnapshot(snapshot)).ToList(); Assert.Equal(expectedSnapshots, snapshots); }; if (test.IsError) { // TODO: Should we actually check that it's only the last response that causes the exception? var exception = await Assert.ThrowsAnyAsync <Exception>(action); Assert.True(exception is ArgumentException || exception is InvalidOperationException, $"Exception type: {exception.GetType()}"); } else { await action(); } // Different clients use different watch target IDs. The test data always uses 1 // to mean "the target ID that the client uses". void ReplaceWatchTargetId(RepeatedField <int> ids) { if (ids == null) { return; } for (int i = 0; i < ids.Count; i++) { if (ids[i] == 1) { ids[i] = WatchStream.WatchTargetId; } } } // Converts from a test proto snapshot to a QuerySnapshot QuerySnapshot ConvertSnapshot(Snapshot snapshot) { var readTime = Timestamp.FromProto(snapshot.ReadTime); var changes = snapshot.Changes.Select(change => ConvertChange(change, readTime)).ToList(); var docs = snapshot.Docs.Select(doc => DocumentSnapshot.ForDocument(db, doc, readTime)).ToList(); return(QuerySnapshot.ForChanges(query, docs, changes, readTime)); } DocumentChange ConvertChange(DocChange change, Timestamp readTime) { var snapshot = DocumentSnapshot.ForDocument(db, change.Doc, readTime); return(new DocumentChange(snapshot, (DocumentChange.Type)change.Kind, change.OldIndex == -1 ? default(int?) : change.OldIndex, change.NewIndex == -1 ? default(int?) : change.NewIndex)); } }
public void Query(SerializableTest wrapper) { QueryTest test = wrapper.Test.Query; if (test.IsError) { var exception = Assert.ThrowsAny <Exception>(() => BuildQuery()); Assert.True(exception is ArgumentException || exception is InvalidOperationException, $"Exception type: {exception.GetType()}"); } else { var query = BuildQuery(); Assert.Equal(test.Query, query.ToStructuredQuery()); } Query BuildQuery() { Query query = GetCollectionReference(test.CollPath); foreach (var clause in test.Clauses) { switch (clause.ClauseCase) { case Clause.ClauseOneofCase.EndAt: query = query.EndAt(ConvertCursor(clause.EndAt)); break; case Clause.ClauseOneofCase.EndBefore: query = query.EndBefore(ConvertCursor(clause.EndBefore)); break; case Clause.ClauseOneofCase.Limit: query = query.Limit(clause.Limit); break; case Clause.ClauseOneofCase.Offset: query = query.Offset(clause.Offset); break; case Clause.ClauseOneofCase.OrderBy: var ordering = clause.OrderBy; var path = ConvertFieldPath(ordering.Path); query = ordering.Direction == "asc" ? query.OrderBy(path) : query.OrderByDescending(path); break; case Clause.ClauseOneofCase.Select: query = query.Select(clause.Select.Fields.Select(ConvertFieldPath).ToArray()); break; case Clause.ClauseOneofCase.StartAfter: query = query.StartAfter(ConvertCursor(clause.StartAfter)); break; case Clause.ClauseOneofCase.StartAt: query = query.StartAt(ConvertCursor(clause.StartAt)); break; case Clause.ClauseOneofCase.Where: var filterPath = ConvertFieldPath(clause.Where.Path); if (!QueryOperators.TryGetValue(clause.Where.Op, out var filterProvider)) { throw new ArgumentException($"Invalid where operator: {clause.Where.Op}"); } var value = DeserializeJson(clause.Where.JsonValue); query = filterProvider(query, filterPath, value); break; default: throw new InvalidOperationException($"Unexpected clause case: {clause.ClauseCase}"); } } return(query); } // Note: dynamic to allow a DocumentSnapshot to be returned and used in overload resolution. dynamic ConvertCursor(Cursor cursor) { var docSnapshot = cursor.DocSnapshot; if (docSnapshot == null) { return(cursor.JsonValues.Select(DeserializeJson).ToArray()); } var docRef = GetDocumentReference(docSnapshot.Path); return(DocumentSnapshot.ForDocument( docRef.Database, new Document { Name = docRef.Path, Fields = { ValueSerializer.SerializeMap(DeserializeJson(cursor.DocSnapshot.JsonData)) }, CreateTime = wkt::Timestamp.FromDateTimeOffset(DateTimeOffset.MinValue), UpdateTime = wkt::Timestamp.FromDateTimeOffset(DateTimeOffset.MinValue), }, Timestamp.FromDateTimeOffset(DateTimeOffset.MinValue))); } }