Skip to content
Merged
7 changes: 6 additions & 1 deletion lib/src/firestore/firestore_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,12 @@ class FirestoreNamespace extends FunctionsNamespace {
final params = _extractParams(document, headers.documentPath);

final parsed = await _parseBody(request);
final snapshot = parsed?['value'];
final snapshotKey =
(methodName == 'onDocumentDeleted' ||
methodName == 'onDocumentDeletedWithAuthContext')
? 'old_value'
: 'value';
final snapshot = parsed?[snapshotKey];

try {
if (withAuthContext) {
Expand Down
34 changes: 33 additions & 1 deletion test/e2e/tests/firestore_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,39 @@ void runFirestoreTests(
await client.deleteDocument('users/$testUserId');
print('✓ Document deleted successfully');

// Wait for trigger
// Wait for trigger to fire and write result
await Future<void>.delayed(const Duration(seconds: 2));

// Verify document no longer exists
final doc = await client.getDocument('users/$testUserId');
expect(doc, isNull, reason: 'Document should not exist after deletion');
print('✓ Document deletion verified');

// Poll for the side-channel result doc written by the handler
// (up to 5 attempts × 500ms = 2.5s extra headroom)
Map<String, dynamic>? result;
for (var i = 0; i < 5; i++) {
result = await client.getDocument(
'trigger_results/deleted_$testUserId',
);
if (result != null) break;
await Future<void>.delayed(const Duration(milliseconds: 500));
}

expect(
result,
isNotNull,
reason: 'trigger_results doc should have been written by handler',
);
expect(
result!['fields']['hasData']['booleanValue'],
isTrue,
reason: 'event.data should be non-null for delete events',
);
expect(result['fields']['name']['stringValue'], 'To Be Deleted');
expect(result['fields']['email']['stringValue'], 'delete@example.com');
expect(result['fields']['finalMessage']['stringValue'], 'goodbye');
print('✓ Handler received correct pre-deletion document data');
});

test('onDocumentWritten fires for all operations', () async {
Expand Down Expand Up @@ -283,6 +309,12 @@ void runFirestoreTests(
} catch (e) {
// Ignore errors during cleanup
}
// Clean up side-channel result doc written by the onDocumentDeleted handler
try {
await client.deleteDocument('trigger_results/deleted_$testUserId');
} catch (e) {
// Ignore errors during cleanup
}
});
});
}
40 changes: 40 additions & 0 deletions test/fixtures/dart_reference/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:io';

import 'package:firebase_functions/firebase_functions.dart';

// =============================================================================
Expand Down Expand Up @@ -184,6 +187,43 @@ void main(List<String> args) async {
final data = event.data?.data();
print('Document deleted: users/${event.params['userId']}');
print(' Final data: $data');

// write result to Firestore so the e2e test can assert on
// what the handler actually received (exposes the old_value/value).
final userId = event.params['userId'] as String;
final firestoreHost =
Platform.environment['FIRESTORE_EMULATOR_HOST'] ?? '127.0.0.1:8080';
final parts = firestoreHost.split(':');
final host = parts[0];
final port = int.tryParse(parts.length > 1 ? parts[1] : '8080') ?? 8080;

final resultFields = <String, dynamic>{
'hasData': {'booleanValue': data != null},
'name': {'stringValue': data?['name']?.toString() ?? ''},
'email': {'stringValue': data?['email']?.toString() ?? ''},
'finalMessage': {
'stringValue': data?['finalMessage']?.toString() ?? '',
},
};

try {
final httpClient = HttpClient();
final req = await httpClient.post(
host,
port,
'/v1/projects/demo-test/databases/(default)/documents/'
'trigger_results?documentId=deleted_$userId',
);
req.headers.contentType = ContentType.json;
req.write(jsonEncode({'fields': resultFields}));
await req.close();
httpClient.close();
} catch (e) {
print(
' Firestore write to verify handler '
'received event.data failed: $e',
);
}
});

firebase.firestore.onDocumentWritten(document: 'users/{userId}', (
Expand Down
Loading