feat(attendance): add notification delivery and NFC security foundation

This commit is contained in:
zouantchaw
2026-03-16 17:06:17 +01:00
parent 5d8240ed51
commit 73287f42bd
21 changed files with 1734 additions and 36 deletions

View File

@@ -153,6 +153,50 @@ async function main() {
assert.equal(clientSession.business.businessId, fixture.business.id);
logStep('client.session.ok', clientSession);
const clientPushTokenPrimary = await apiCall('/client/devices/push-tokens', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('client-push-token-primary'),
body: {
provider: 'FCM',
platform: 'IOS',
pushToken: `smoke-client-primary-${Date.now()}-abcdefghijklmnop`,
deviceId: 'smoke-client-iphone-15-pro',
appVersion: '2.0.0-smoke',
appBuild: '2000',
locale: 'en-US',
timezone: 'America/Los_Angeles',
},
});
assert.ok(clientPushTokenPrimary.tokenId);
logStep('client.push-token.register-primary.ok', clientPushTokenPrimary);
const clientPushTokenCleanup = await apiCall('/client/devices/push-tokens', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('client-push-token-cleanup'),
body: {
provider: 'FCM',
platform: 'ANDROID',
pushToken: `smoke-client-cleanup-${Date.now()}-abcdefghijklmnop`,
deviceId: 'smoke-client-pixel-9',
appVersion: '2.0.0-smoke',
appBuild: '2001',
locale: 'en-US',
timezone: 'America/Los_Angeles',
},
});
assert.ok(clientPushTokenCleanup.tokenId);
logStep('client.push-token.register-cleanup.ok', clientPushTokenCleanup);
const clientPushTokenDeleted = await apiCall(`/client/devices/push-tokens?tokenId=${encodeURIComponent(clientPushTokenCleanup.tokenId)}&reason=SMOKE_CLEANUP`, {
method: 'DELETE',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('client-push-token-delete'),
});
assert.equal(clientPushTokenDeleted.removedCount, 1);
logStep('client.push-token.delete.ok', clientPushTokenDeleted);
const clientDashboard = await apiCall('/client/dashboard', {
token: ownerSession.sessionToken,
});
@@ -517,6 +561,50 @@ async function main() {
assert.equal(staffSession.staff.staffId, fixture.staff.ana.id);
logStep('staff.session.ok', staffSession);
const staffPushTokenPrimary = await apiCall('/staff/devices/push-tokens', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-push-token-primary'),
body: {
provider: 'FCM',
platform: 'IOS',
pushToken: `smoke-staff-primary-${Date.now()}-abcdefghijklmnop`,
deviceId: 'smoke-staff-iphone-15-pro',
appVersion: '2.0.0-smoke',
appBuild: '2000',
locale: 'en-US',
timezone: 'America/Los_Angeles',
},
});
assert.ok(staffPushTokenPrimary.tokenId);
logStep('staff.push-token.register-primary.ok', staffPushTokenPrimary);
const staffPushTokenCleanup = await apiCall('/staff/devices/push-tokens', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-push-token-cleanup'),
body: {
provider: 'FCM',
platform: 'ANDROID',
pushToken: `smoke-staff-cleanup-${Date.now()}-abcdefghijklmnop`,
deviceId: 'smoke-staff-pixel-9',
appVersion: '2.0.0-smoke',
appBuild: '2001',
locale: 'en-US',
timezone: 'America/Los_Angeles',
},
});
assert.ok(staffPushTokenCleanup.tokenId);
logStep('staff.push-token.register-cleanup.ok', staffPushTokenCleanup);
const staffPushTokenDeleted = await apiCall(`/staff/devices/push-tokens?tokenId=${encodeURIComponent(staffPushTokenCleanup.tokenId)}&reason=SMOKE_CLEANUP`, {
method: 'DELETE',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-push-token-delete'),
});
assert.equal(staffPushTokenDeleted.removedCount, 1);
logStep('staff.push-token.delete.ok', staffPushTokenDeleted);
const staffDashboard = await apiCall('/staff/dashboard', {
token: staffAuth.idToken,
});
@@ -871,6 +959,8 @@ async function main() {
latitude: fixture.clockPoint.latitude + 0.0075,
longitude: fixture.clockPoint.longitude + 0.0075,
accuracyMeters: 8,
proofNonce: uniqueKey('geo-proof-clock-in'),
proofTimestamp: isoTimestamp(0),
overrideReason: 'Parking garage entrance is outside the marked hub geofence',
capturedAt: isoTimestamp(0),
},
@@ -878,6 +968,7 @@ async function main() {
assert.equal(clockIn.validationStatus, 'FLAGGED');
assert.equal(clockIn.effectiveClockInMode, fixture.shifts.assigned.clockInMode);
assert.equal(clockIn.overrideUsed, true);
assert.ok(clockIn.securityProofId);
logStep('staff.clock-in.ok', clockIn);
const attendanceStatusAfterClockIn = await apiCall('/staff/clock-in/status', {
@@ -950,10 +1041,13 @@ async function main() {
latitude: fixture.clockPoint.latitude,
longitude: fixture.clockPoint.longitude,
accuracyMeters: 10,
proofNonce: uniqueKey('geo-proof-clock-out'),
proofTimestamp: isoTimestamp(1),
breakMinutes: 30,
capturedAt: isoTimestamp(1),
},
});
assert.ok(clockOut.securityProofId);
logStep('staff.clock-out.ok', clockOut);
const requestedSwap = await apiCall(`/staff/shifts/${fixture.shifts.assigned.id}/request-swap`, {