Files
Krow-workspace/backend/unified-api/scripts/live-smoke-v2-unified.mjs

1652 lines
57 KiB
JavaScript

import assert from 'node:assert/strict';
import { signInWithPassword } from '../src/services/identity-toolkit.js';
import { V2DemoFixture as fixture } from '../../command-api/scripts/v2-demo-fixture.mjs';
const unifiedBaseUrl = process.env.UNIFIED_API_BASE_URL || 'https://krow-api-v2-e3g6witsvq-uc.a.run.app';
const ownerEmail = process.env.V2_DEMO_OWNER_EMAIL || 'legendary.owner+v2@krowd.com';
const staffEmail = process.env.V2_DEMO_STAFF_EMAIL || 'ana.barista+v2@krowd.com';
const staffBenEmail = process.env.V2_DEMO_STAFF_BEN_EMAIL || 'ben.barista+v2@krowd.com';
const ownerPassword = process.env.V2_DEMO_OWNER_PASSWORD || 'Demo2026!';
const staffPassword = process.env.V2_DEMO_STAFF_PASSWORD || 'Demo2026!';
const staffBenPassword = process.env.V2_DEMO_STAFF_BEN_PASSWORD || 'Demo2026!';
function uniqueKey(prefix) {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}
function isoDate(offsetDays = 0) {
const value = new Date(Date.now() + (offsetDays * 24 * 60 * 60 * 1000));
return value.toISOString().slice(0, 10);
}
function isoTimestamp(offsetHours = 0) {
return new Date(Date.now() + (offsetHours * 60 * 60 * 1000)).toISOString();
}
function logStep(step, payload) {
// eslint-disable-next-line no-console
console.log(`[unified-smoke-v2] ${step}: ${JSON.stringify(payload)}`);
}
async function readJson(response) {
const text = await response.text();
return text ? JSON.parse(text) : {};
}
async function apiCall(path, {
method = 'GET',
token,
idempotencyKey,
body,
expectedStatus = 200,
allowFailure = false,
} = {}) {
const headers = {};
if (token) headers.Authorization = `Bearer ${token}`;
if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
if (body !== undefined) headers['Content-Type'] = 'application/json';
const response = await fetch(`${unifiedBaseUrl}${path}`, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
});
const payload = await readJson(response);
if (allowFailure) {
return {
statusCode: response.status,
body: payload,
};
}
if (response.status !== expectedStatus) {
throw new Error(`${method} ${path} expected ${expectedStatus}, got ${response.status}: ${JSON.stringify(payload)}`);
}
return payload;
}
async function uploadFile(path, token, {
filename,
contentType,
content,
fields = {},
expectedStatus = 200,
}) {
const form = new FormData();
for (const [key, value] of Object.entries(fields)) {
form.set(key, value);
}
form.set(
'file',
new File([content], filename, {
type: contentType,
})
);
const response = await fetch(`${unifiedBaseUrl}${path}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body: form,
});
const payload = await readJson(response);
if (response.status !== expectedStatus) {
throw new Error(`POST ${path} expected ${expectedStatus}, got ${response.status}: ${JSON.stringify(payload)}`);
}
return payload;
}
async function finalizeVerifiedUpload({
token,
uploadCategory,
filename,
contentType,
content,
finalizePath,
finalizeMethod = 'PUT',
verificationType,
subjectId,
rules = {},
finalizeBody = {},
}) {
const uploaded = await uploadFile('/upload-file', token, {
filename,
contentType,
content,
fields: {
visibility: 'private',
category: uploadCategory,
},
});
const signed = await apiCall('/create-signed-url', {
method: 'POST',
token,
body: {
fileUri: uploaded.fileUri,
expiresInSeconds: 300,
},
});
const verification = await apiCall('/verifications', {
method: 'POST',
token,
body: {
type: verificationType,
subjectType: 'worker',
subjectId,
fileUri: uploaded.fileUri,
rules,
},
expectedStatus: 202,
});
const finalized = await apiCall(finalizePath, {
method: finalizeMethod,
token,
body: {
...finalizeBody,
verificationId: verification.verificationId,
fileUri: signed.signedUrl,
photoUrl: signed.signedUrl,
},
});
return {
uploaded,
signed,
verification,
finalized,
};
}
async function approveVerification({
token,
verificationId,
note = 'Smoke approval',
}) {
return apiCall(`/verifications/${verificationId}/review`, {
method: 'POST',
token,
body: {
decision: 'APPROVED',
note,
reasonCode: 'SMOKE_APPROVAL',
},
});
}
async function signInClient() {
return apiCall('/auth/client/sign-in', {
method: 'POST',
body: {
email: ownerEmail,
password: ownerPassword,
},
});
}
async function signInStaff() {
return signInWithPassword({
email: staffEmail,
password: staffPassword,
});
}
async function signInStaffBen() {
return signInWithPassword({
email: staffBenEmail,
password: staffBenPassword,
});
}
async function main() {
const reportWindow = `startDate=${encodeURIComponent(isoTimestamp(-24 * 14))}&endDate=${encodeURIComponent(isoTimestamp(24 * 14))}`;
const ownerSession = await signInClient();
const staffAuth = await signInStaff();
const staffBenAuth = await signInStaffBen();
assert.ok(ownerSession.sessionToken);
assert.ok(staffAuth.idToken);
assert.ok(staffBenAuth.idToken);
assert.equal(ownerSession.business.businessId, fixture.business.id);
logStep('auth.client.sign-in.ok', {
tenantId: ownerSession.tenant.tenantId,
businessId: ownerSession.business.businessId,
});
logStep('auth.staff.password-sign-in.ok', {
uid: staffAuth.localId,
email: staffEmail,
});
logStep('auth.staff-b.password-sign-in.ok', {
uid: staffBenAuth.localId,
email: staffBenEmail,
});
const authSession = await apiCall('/auth/session', {
token: ownerSession.sessionToken,
});
assert.equal(authSession.business.businessId, fixture.business.id);
logStep('auth.session.ok', authSession);
const staffPhoneStart = await apiCall('/auth/staff/phone/start', {
method: 'POST',
body: { phoneNumber: fixture.staff.ana.phone },
});
assert.equal(staffPhoneStart.mode, 'CLIENT_FIREBASE_SDK');
logStep('auth.staff.phone-start.ok', staffPhoneStart);
const staffPhoneVerify = await apiCall('/auth/staff/phone/verify', {
method: 'POST',
body: {
mode: 'sign-in',
idToken: staffAuth.idToken,
},
});
assert.equal(staffPhoneVerify.staff.staffId, fixture.staff.ana.id);
logStep('auth.staff.phone-verify.ok', {
staffId: staffPhoneVerify.staff.staffId,
requiresProfileSetup: staffPhoneVerify.requiresProfileSetup,
});
const clientSession = await apiCall('/client/session', {
token: ownerSession.sessionToken,
});
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,
});
assert.equal(clientDashboard.businessId, fixture.business.id);
logStep('client.dashboard.ok', {
weeklySpendCents: clientDashboard.spending.weeklySpendCents,
openPositionsToday: clientDashboard.coverage.openPositionsToday,
});
const clientReorders = await apiCall('/client/reorders', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(clientReorders.items));
if (clientReorders.items[0]) {
assert.equal(typeof clientReorders.items[0].hourlyRateCents, 'number');
assert.equal(typeof clientReorders.items[0].totalPriceCents, 'number');
}
logStep('client.reorders.ok', { count: clientReorders.items.length });
const billingAccounts = await apiCall('/client/billing/accounts', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(billingAccounts.items));
logStep('client.billing.accounts.ok', { count: billingAccounts.items.length });
const pendingInvoices = await apiCall('/client/billing/invoices/pending', {
token: ownerSession.sessionToken,
});
assert.ok(pendingInvoices.items.length >= 1);
const invoiceId = pendingInvoices.items[0].invoiceId;
logStep('client.billing.pending-invoices.ok', { count: pendingInvoices.items.length, invoiceId });
const invoiceHistory = await apiCall('/client/billing/invoices/history', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(invoiceHistory.items));
logStep('client.billing.invoice-history.ok', { count: invoiceHistory.items.length });
const currentBill = await apiCall('/client/billing/current-bill', {
token: ownerSession.sessionToken,
});
assert.ok(typeof currentBill.currentBillCents === 'number');
logStep('client.billing.current-bill.ok', currentBill);
const savings = await apiCall('/client/billing/savings', {
token: ownerSession.sessionToken,
});
assert.ok(typeof savings.savingsCents === 'number');
logStep('client.billing.savings.ok', savings);
const spendBreakdown = await apiCall('/client/billing/spend-breakdown?period=month', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(spendBreakdown.items));
logStep('client.billing.spend-breakdown.ok', { count: spendBreakdown.items.length });
const coverage = await apiCall(`/client/coverage?date=${isoDate(0)}`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(coverage.items));
const seededCoverageShift = coverage.items.find((item) => item.shiftId === fixture.shifts.assigned.id) || coverage.items[0];
assert.ok(seededCoverageShift);
assert.ok(seededCoverageShift.locationName);
if (seededCoverageShift.assignedWorkers?.[0]) {
assert.equal(typeof seededCoverageShift.assignedWorkers[0].hasReview, 'boolean');
}
logStep('client.coverage.ok', { count: coverage.items.length });
const coverageStats = await apiCall(`/client/coverage/stats?date=${isoDate(0)}`, {
token: ownerSession.sessionToken,
});
assert.ok(typeof coverageStats.totalCoveragePercentage === 'number');
logStep('client.coverage.stats.ok', coverageStats);
const coreTeam = await apiCall('/client/coverage/core-team', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(coreTeam.items));
logStep('client.coverage.core-team.ok', { count: coreTeam.items.length });
const dispatchTeams = await apiCall('/client/coverage/dispatch-teams', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(dispatchTeams.items));
assert.ok(dispatchTeams.items.length >= 2);
logStep('client.coverage.dispatch-teams.ok', { count: dispatchTeams.items.length });
const coverageIncidentsBefore = await apiCall(`/client/coverage/incidents?${reportWindow}`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(coverageIncidentsBefore.items));
assert.ok(coverageIncidentsBefore.items.length >= 1);
logStep('client.coverage.incidents-before.ok', { count: coverageIncidentsBefore.items.length });
const hubs = await apiCall('/client/hubs', {
token: ownerSession.sessionToken,
});
const seededHub = hubs.items.find((hub) => hub.hubId === fixture.clockPoint.id);
assert.ok(seededHub);
assert.equal(seededHub.clockInMode, fixture.clockPoint.defaultClockInMode);
assert.equal(seededHub.allowClockInOverride, fixture.clockPoint.allowClockInOverride);
logStep('client.hubs.ok', { count: hubs.items.length });
const costCenters = await apiCall('/client/cost-centers', {
token: ownerSession.sessionToken,
});
assert.ok(costCenters.items.length >= 1);
logStep('client.cost-centers.ok', { count: costCenters.items.length });
const vendors = await apiCall('/client/vendors', {
token: ownerSession.sessionToken,
});
assert.ok(vendors.items.length >= 1);
logStep('client.vendors.ok', { count: vendors.items.length });
const vendorRoles = await apiCall(`/client/vendors/${fixture.vendor.id}/roles`, {
token: ownerSession.sessionToken,
});
assert.ok(vendorRoles.items.length >= 1);
logStep('client.vendor-roles.ok', { count: vendorRoles.items.length });
const hubManagers = await apiCall(`/client/hubs/${fixture.clockPoint.id}/managers`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(hubManagers.items));
logStep('client.hub-managers.ok', { count: hubManagers.items.length });
const teamMembers = await apiCall('/client/team-members', {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(teamMembers.items));
logStep('client.team-members.ok', { count: teamMembers.items.length });
const createdShiftManager = await apiCall('/client/shift-managers', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('create-shift-manager'),
body: {
hubId: fixture.clockPoint.id,
email: `smoke.manager.${Date.now()}@krowd.com`,
firstName: 'Smoke',
lastName: 'Manager',
phone: '+15550009999',
},
});
assert.ok(createdShiftManager.businessMembershipId);
assert.equal(createdShiftManager.membershipStatus, 'INVITED');
assert.ok(createdShiftManager.managerAssignmentId);
logStep('client.shift-manager.create.ok', createdShiftManager);
const teamMembersAfterCreate = await apiCall('/client/team-members', {
token: ownerSession.sessionToken,
});
assert.ok(teamMembersAfterCreate.items.some((item) => item.businessMembershipId === createdShiftManager.businessMembershipId));
logStep('client.team-members.after-create.ok', { count: teamMembersAfterCreate.items.length });
const viewedOrders = await apiCall(`/client/orders/view?${reportWindow}`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(viewedOrders.items));
if (viewedOrders.items[0]) {
assert.ok(viewedOrders.items[0].clientName);
assert.equal(typeof viewedOrders.items[0].hourlyRate, 'number');
}
logStep('client.orders.view.ok', { count: viewedOrders.items.length });
const scheduledShifts = await apiCall(`/client/shifts/scheduled?${reportWindow}`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(scheduledShifts.items));
assert.equal(scheduledShifts.items.length, viewedOrders.items.length);
if (viewedOrders.items[0] && scheduledShifts.items[0]) {
assert.equal(scheduledShifts.items[0].itemId, viewedOrders.items[0].itemId);
}
logStep('client.shifts.scheduled.ok', { count: scheduledShifts.items.length });
const reorderPreview = await apiCall(`/client/orders/${fixture.orders.completed.id}/reorder-preview`, {
token: ownerSession.sessionToken,
});
assert.equal(reorderPreview.orderId, fixture.orders.completed.id);
logStep('client.orders.reorder-preview.ok', reorderPreview);
const reportSummary = await apiCall(`/client/reports/summary?${reportWindow}`, {
token: ownerSession.sessionToken,
});
assert.ok(typeof reportSummary.totalShifts === 'number');
logStep('client.reports.summary.ok', reportSummary);
const dailyOps = await apiCall(`/client/reports/daily-ops?date=${isoDate(0)}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.daily-ops.ok', dailyOps);
const spendReport = await apiCall(`/client/reports/spend?${reportWindow}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.spend.ok', spendReport);
const coverageReport = await apiCall(`/client/reports/coverage?${reportWindow}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.coverage.ok', coverageReport);
const forecastReport = await apiCall(`/client/reports/forecast?${reportWindow}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.forecast.ok', forecastReport);
const performanceReport = await apiCall(`/client/reports/performance?${reportWindow}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.performance.ok', performanceReport);
const noShowReport = await apiCall(`/client/reports/no-show?${reportWindow}`, {
token: ownerSession.sessionToken,
});
logStep('client.reports.no-show.ok', noShowReport);
const createdHub = await apiCall('/client/hubs', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('create-hub'),
body: {
name: `Smoke Hub ${Date.now()}`,
fullAddress: '500 Castro Street, Mountain View, CA',
latitude: 37.3925,
longitude: -122.0782,
city: 'Mountain View',
state: 'CA',
country: 'US',
zipCode: '94041',
costCenterId: fixture.costCenters.cafeOps.id,
geofenceRadiusMeters: 100,
},
});
assert.ok(createdHub.hubId);
logStep('client.hubs.create.ok', createdHub);
const updatedHub = await apiCall(`/client/hubs/${createdHub.hubId}`, {
method: 'PUT',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('update-hub'),
body: {
name: `${createdHub.name || 'Smoke Hub'} Updated`,
geofenceRadiusMeters: 140,
},
});
logStep('client.hubs.update.ok', updatedHub);
const assignedHubManager = await apiCall(`/client/hubs/${createdHub.hubId}/managers`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('assign-hub-manager'),
body: {
managerUserId: fixture.users.operationsManager.id,
},
});
logStep('client.hubs.assign-manager.ok', assignedHubManager);
const assignedNfc = await apiCall(`/client/hubs/${createdHub.hubId}/assign-nfc`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('assign-hub-nfc'),
body: {
nfcTagId: `NFC-SMOKE-${Date.now()}`,
},
});
logStep('client.hubs.assign-nfc.ok', assignedNfc);
const deletedHub = await apiCall(`/client/hubs/${createdHub.hubId}`, {
method: 'DELETE',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('delete-hub'),
body: {
reason: 'smoke cleanup',
},
});
logStep('client.hubs.delete.ok', deletedHub);
const disputedInvoice = await apiCall(`/client/billing/invoices/${invoiceId}/dispute`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('invoice-dispute'),
body: {
reason: 'Smoke dispute before approval',
},
});
logStep('client.billing.invoice-dispute.ok', disputedInvoice);
const approvedInvoice = await apiCall(`/client/billing/invoices/${invoiceId}/approve`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('invoice-approve'),
body: {},
});
logStep('client.billing.invoice-approve.ok', approvedInvoice);
const createdOneTimeOrder = await apiCall('/client/orders/one-time', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('order-one-time'),
body: {
hubId: fixture.clockPoint.id,
vendorId: fixture.vendor.id,
eventName: `Smoke One-Time ${Date.now()}`,
orderDate: isoDate(3),
serviceType: 'RESTAURANT',
positions: [
{
roleId: fixture.roles.barista.id,
startTime: '08:00',
endTime: '16:00',
workerCount: 1,
payRateCents: 2200,
billRateCents: 3500,
},
],
},
});
assert.ok(createdOneTimeOrder.orderId);
logStep('client.orders.create-one-time.ok', createdOneTimeOrder);
const createdRecurringOrder = await apiCall('/client/orders/recurring', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('order-recurring'),
body: {
hubId: fixture.clockPoint.id,
vendorId: fixture.vendor.id,
eventName: `Smoke Recurring ${Date.now()}`,
startDate: isoDate(5),
endDate: isoDate(10),
recurrenceDays: [1, 3, 5],
serviceType: 'RESTAURANT',
positions: [
{
roleId: fixture.roles.barista.id,
startTime: '09:00',
endTime: '15:00',
workersNeeded: 1,
payRateCents: 2300,
billRateCents: 3600,
},
],
},
});
assert.ok(createdRecurringOrder.orderId);
logStep('client.orders.create-recurring.ok', createdRecurringOrder);
const createdPermanentOrder = await apiCall('/client/orders/permanent', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('order-permanent'),
body: {
hubId: fixture.clockPoint.id,
vendorId: fixture.vendor.id,
eventName: `Smoke Permanent ${Date.now()}`,
startDate: isoDate(7),
daysOfWeek: [1, 2, 3, 4, 5],
horizonDays: 14,
serviceType: 'RESTAURANT',
positions: [
{
roleId: fixture.roles.barista.id,
startTime: '07:00',
endTime: '13:00',
workersNeeded: 1,
payRateCents: 2200,
billRateCents: 3500,
},
],
},
});
assert.ok(createdPermanentOrder.orderId);
logStep('client.orders.create-permanent.ok', createdPermanentOrder);
const editedOrderCopy = await apiCall(`/client/orders/${createdRecurringOrder.orderId}/edit`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('order-edit'),
body: {
eventName: `Edited Copy ${Date.now()}`,
},
});
assert.ok(editedOrderCopy.orderId);
assert.notEqual(editedOrderCopy.orderId, createdRecurringOrder.orderId);
logStep('client.orders.edit-copy.ok', editedOrderCopy);
const cancelledOrder = await apiCall(`/client/orders/${createdOneTimeOrder.orderId}/cancel`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('order-cancel'),
body: {
reason: 'Smoke cancel validation',
},
});
assert.equal(cancelledOrder.futureOnly, true);
logStep('client.orders.cancel.ok', cancelledOrder);
const coverageReview = await apiCall('/client/coverage/reviews', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('coverage-review'),
body: {
staffId: fixture.staff.ana.id,
assignmentId: fixture.assignments.completedAna.id,
rating: 5,
markAsFavorite: true,
issueFlags: [],
feedback: 'Smoke review',
},
});
logStep('client.coverage.review.ok', coverageReview);
const staffSession = await apiCall('/staff/session', {
token: staffAuth.idToken,
});
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,
});
assert.ok(Array.isArray(staffDashboard.recommendedShifts));
if (staffDashboard.todaysShifts[0]) {
assert.ok(staffDashboard.todaysShifts[0].clientName);
assert.equal(typeof staffDashboard.todaysShifts[0].totalRate, 'number');
}
if (staffDashboard.recommendedShifts[0]) {
assert.ok(staffDashboard.recommendedShifts[0].clientName);
assert.equal(typeof staffDashboard.recommendedShifts[0].totalRate, 'number');
}
logStep('staff.dashboard.ok', {
todaysShifts: staffDashboard.todaysShifts.length,
recommendedShifts: staffDashboard.recommendedShifts.length,
});
const staffProfileCompletion = await apiCall('/staff/profile-completion', {
token: staffAuth.idToken,
});
logStep('staff.profile-completion.ok', staffProfileCompletion);
const staffProfileStats = await apiCall('/staff/profile/stats', {
token: staffAuth.idToken,
});
assert.equal(typeof staffProfileStats.totalShifts, 'number');
assert.equal(typeof staffProfileStats.reliabilityScore, 'number');
logStep('staff.profile.stats.ok', staffProfileStats);
const staffAvailability = await apiCall('/staff/availability', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(staffAvailability.items));
logStep('staff.availability.ok', { count: staffAvailability.items.length });
const todaysShifts = await apiCall('/staff/clock-in/shifts/today', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(todaysShifts.items));
const assignedTodayShift = todaysShifts.items.find((shift) => shift.shiftId === fixture.shifts.assigned.id);
assert.ok(assignedTodayShift);
assert.equal(assignedTodayShift.clientName, fixture.business.name);
assert.equal(typeof assignedTodayShift.hourlyRate, 'number');
assert.equal(typeof assignedTodayShift.latitude, 'number');
assert.equal(typeof assignedTodayShift.longitude, 'number');
assert.equal(assignedTodayShift.clockInMode, fixture.shifts.assigned.clockInMode);
assert.equal(assignedTodayShift.allowClockInOverride, fixture.shifts.assigned.allowClockInOverride);
const clockableTodayShift = todaysShifts.items.find((shift) => shift.attendanceStatus === 'NOT_CLOCKED_IN')
|| assignedTodayShift;
logStep('staff.clock-in.shifts-today.ok', { count: todaysShifts.items.length });
const attendanceStatusBefore = await apiCall('/staff/clock-in/status', {
token: staffAuth.idToken,
});
logStep('staff.clock-in.status-before.ok', attendanceStatusBefore);
const paymentsSummary = await apiCall('/staff/payments/summary?period=month', {
token: staffAuth.idToken,
});
logStep('staff.payments.summary.ok', paymentsSummary);
const paymentsHistory = await apiCall('/staff/payments/history?period=month', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(paymentsHistory.items));
logStep('staff.payments.history.ok', { count: paymentsHistory.items.length });
const paymentsChart = await apiCall('/staff/payments/chart?period=month', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(paymentsChart.items));
logStep('staff.payments.chart.ok', { count: paymentsChart.items.length });
const assignedShifts = await apiCall(`/staff/shifts/assigned?${reportWindow}`, {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(assignedShifts.items));
logStep('staff.shifts.assigned.ok', { count: assignedShifts.items.length });
const availableOrders = await apiCall('/staff/orders/available?limit=20', {
token: staffAuth.idToken,
});
assert.ok(availableOrders.items.length > 0);
let ineligibleOrder = null;
let ineligibleOrderDetail = null;
for (const item of availableOrders.items) {
const detail = await apiCall(`/staff/orders/${item.orderId}`, {
token: staffAuth.idToken,
});
if (!ineligibleOrderDetail && detail.eligibility?.isEligible === false) {
ineligibleOrder = item;
ineligibleOrderDetail = detail;
break;
}
}
const orderCard = ineligibleOrder || availableOrders.items[0];
const orderDetail = ineligibleOrderDetail || await apiCall(`/staff/orders/${orderCard.orderId}`, {
token: staffAuth.idToken,
});
assert.ok(orderCard.roleId);
logStep('staff.orders.available.ok', { count: availableOrders.items.length, orderId: orderCard.orderId });
assert.equal(orderDetail.orderId, orderCard.orderId);
assert.equal(orderDetail.roleId, orderCard.roleId);
assert.ok(orderDetail.clientName);
assert.ok(orderDetail.schedule);
assert.ok(orderDetail.location);
assert.ok(Array.isArray(orderDetail.managers));
assert.ok(orderDetail.eligibility);
logStep('staff.orders.detail.ok', {
orderId: orderDetail.orderId,
status: orderDetail.status,
isEligible: orderDetail.eligibility.isEligible,
});
if (orderDetail.eligibility?.isEligible === false) {
const rejectedIneligibleBooking = await apiCall(`/staff/orders/${orderCard.orderId}/book`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-order-book-ineligible'),
body: {
roleId: orderDetail.roleId,
},
allowFailure: true,
});
assert.equal(rejectedIneligibleBooking.statusCode, 422);
assert.equal(rejectedIneligibleBooking.body.code, 'UNPROCESSABLE_ENTITY');
assert.ok(Array.isArray(rejectedIneligibleBooking.body.details?.blockers));
logStep('staff.orders.book.ineligible.rejected.ok', {
orderId: orderCard.orderId,
blockers: rejectedIneligibleBooking.body.details.blockers.length,
});
}
const openShifts = await apiCall('/staff/shifts/open', {
token: staffAuth.idToken,
});
const openShift = openShifts.items.find((shift) => shift.shiftId === fixture.shifts.available.id)
|| openShifts.items[0];
const blockedApplyCandidate = openShifts.items.find((shift) => shift.shiftId !== openShift.shiftId);
assert.ok(openShift);
assert.ok(blockedApplyCandidate);
logStep('staff.shifts.open.ok', { count: openShifts.items.length });
const pendingShifts = await apiCall('/staff/shifts/pending', {
token: staffAuth.idToken,
});
const pendingShift = pendingShifts.items.find((item) => item.shiftId === openShift.shiftId)
|| pendingShifts.items[0];
assert.ok(pendingShift);
logStep('staff.shifts.pending.ok', { count: pendingShifts.items.length });
const cancelledShifts = await apiCall('/staff/shifts/cancelled', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(cancelledShifts.items));
logStep('staff.shifts.cancelled.ok', { count: cancelledShifts.items.length });
const completedShifts = await apiCall('/staff/shifts/completed', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(completedShifts.items));
if (completedShifts.items[0]) {
assert.ok(completedShifts.items[0].clientName);
assert.ok(completedShifts.items[0].date);
assert.ok(completedShifts.items[0].startTime);
assert.ok(completedShifts.items[0].endTime);
assert.equal(typeof completedShifts.items[0].hourlyRate, 'number');
assert.equal(typeof completedShifts.items[0].totalRate, 'number');
}
logStep('staff.shifts.completed.ok', { count: completedShifts.items.length });
const shiftDetail = await apiCall(`/staff/shifts/${openShift.shiftId}`, {
token: staffAuth.idToken,
});
assert.equal(shiftDetail.shiftId, openShift.shiftId);
assert.equal(typeof shiftDetail.latitude, 'number');
assert.equal(typeof shiftDetail.longitude, 'number');
logStep('staff.shifts.detail.ok', shiftDetail);
const profileSections = await apiCall('/staff/profile/sections', {
token: staffAuth.idToken,
});
logStep('staff.profile.sections.ok', profileSections);
const personalInfo = await apiCall('/staff/profile/personal-info', {
token: staffAuth.idToken,
});
logStep('staff.profile.personal-info.ok', personalInfo);
const industries = await apiCall('/staff/profile/industries', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(industries.items));
logStep('staff.profile.industries.ok', industries);
const skills = await apiCall('/staff/profile/skills', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(skills.items));
logStep('staff.profile.skills.ok', skills);
const profileDocumentsBefore = await apiCall('/staff/profile/documents', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(profileDocumentsBefore.items));
assert.ok(profileDocumentsBefore.items.every((item) => item.documentType !== 'ATTIRE'));
logStep('staff.profile.documents-before.ok', { count: profileDocumentsBefore.items.length });
const attireChecklistBefore = await apiCall('/staff/profile/attire', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(attireChecklistBefore.items));
logStep('staff.profile.attire-before.ok', { count: attireChecklistBefore.items.length });
const taxForms = await apiCall('/staff/profile/tax-forms', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(taxForms.items));
logStep('staff.profile.tax-forms.ok', { count: taxForms.items.length });
const emergencyContactsBefore = await apiCall('/staff/profile/emergency-contacts', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(emergencyContactsBefore.items));
logStep('staff.profile.emergency-contacts-before.ok', { count: emergencyContactsBefore.items.length });
const certificatesBefore = await apiCall('/staff/profile/certificates', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(certificatesBefore.items));
logStep('staff.profile.certificates-before.ok', { count: certificatesBefore.items.length });
const bankAccountsBefore = await apiCall('/staff/profile/bank-accounts', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(bankAccountsBefore.items));
logStep('staff.profile.bank-accounts-before.ok', { count: bankAccountsBefore.items.length });
const benefits = await apiCall('/staff/profile/benefits', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(benefits.items));
logStep('staff.profile.benefits.ok', { count: benefits.items.length });
const benefitHistory = await apiCall('/staff/profile/benefits/history?limit=10', {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(benefitHistory.items));
assert.ok(benefitHistory.items.length >= 1);
logStep('staff.profile.benefits.history.ok', { count: benefitHistory.items.length });
const timeCard = await apiCall(`/staff/profile/time-card?month=${new Date().getUTCMonth() + 1}&year=${new Date().getUTCFullYear()}`, {
token: staffAuth.idToken,
});
assert.ok(Array.isArray(timeCard.items));
logStep('staff.profile.time-card.ok', { count: timeCard.items.length });
const privacyBefore = await apiCall('/staff/profile/privacy', {
token: staffAuth.idToken,
});
logStep('staff.profile.privacy-before.ok', privacyBefore);
const faqs = await apiCall('/staff/faqs');
assert.ok(Array.isArray(faqs.items));
logStep('staff.faqs.ok', { count: faqs.items.length });
const faqSearch = await apiCall('/staff/faqs/search?q=payments');
assert.ok(Array.isArray(faqSearch.items));
logStep('staff.faqs.search.ok', { count: faqSearch.items.length });
const updatedAvailability = await apiCall('/staff/availability', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-availability'),
body: {
dayOfWeek: 2,
availabilityStatus: 'PARTIAL',
slots: [{ start: '10:00', end: '18:00' }],
},
});
logStep('staff.availability.update.ok', updatedAvailability);
const quickSetAvailability = await apiCall('/staff/availability/quick-set', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-availability-quick-set'),
body: {
quickSetType: 'weekdays',
startDate: isoTimestamp(0),
endDate: isoTimestamp(24 * 7),
},
});
logStep('staff.availability.quick-set.ok', quickSetAvailability);
const personalInfoUpdate = await apiCall('/staff/profile/personal-info', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-personal-info'),
body: {
firstName: 'Ana',
lastName: 'Barista',
bio: 'Smoke-tested staff bio',
preferredLocations: [
{
label: 'Mountain View',
city: 'Mountain View',
latitude: fixture.clockPoint.latitude,
longitude: fixture.clockPoint.longitude,
radiusMiles: 20,
},
],
phone: fixture.staff.ana.phone,
email: staffEmail,
},
});
logStep('staff.profile.personal-info.update.ok', personalInfoUpdate);
const experienceUpdate = await apiCall('/staff/profile/experience', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-experience'),
body: {
industries: ['CATERING', 'CAFE'],
skills: ['BARISTA', 'CUSTOMER_SERVICE'],
primaryRole: 'BARISTA',
},
});
logStep('staff.profile.experience.update.ok', experienceUpdate);
const locationUpdate = await apiCall('/staff/profile/locations', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-locations'),
body: {
preferredLocations: [
{
label: 'Mountain View',
city: 'Mountain View',
latitude: fixture.clockPoint.latitude,
longitude: fixture.clockPoint.longitude,
radiusMiles: 25,
},
],
maxDistanceMiles: 25,
},
});
logStep('staff.profile.locations.update.ok', locationUpdate);
const createdEmergencyContact = await apiCall('/staff/profile/emergency-contacts', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-emergency-create'),
body: {
fullName: 'Smoke Contact',
phone: '+15550009999',
relationshipType: 'Sibling',
isPrimary: false,
},
});
assert.ok(createdEmergencyContact.contactId);
logStep('staff.profile.emergency-contact.create.ok', createdEmergencyContact);
const updatedEmergencyContact = await apiCall(`/staff/profile/emergency-contacts/${createdEmergencyContact.contactId}`, {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-emergency-update'),
body: {
fullName: 'Smoke Contact Updated',
relationshipType: 'Brother',
},
});
logStep('staff.profile.emergency-contact.update.ok', updatedEmergencyContact);
const savedW4Draft = await apiCall('/staff/profile/tax-forms/w4', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-tax-w4-draft'),
body: {
fields: {
filingStatus: 'single',
},
},
});
logStep('staff.profile.tax-form.w4-draft.ok', savedW4Draft);
const submittedI9 = await apiCall('/staff/profile/tax-forms/i9/submit', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-tax-i9-submit'),
body: {
fields: {
section1Complete: true,
},
},
});
logStep('staff.profile.tax-form.i9-submit.ok', submittedI9);
const addedBankAccount = await apiCall('/staff/profile/bank-accounts', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-bank-account'),
body: {
bankName: 'Demo Credit Union',
accountNumber: '1234567890',
routingNumber: '021000021',
accountType: 'checking',
},
});
logStep('staff.profile.bank-account.add.ok', addedBankAccount);
const updatedPrivacy = await apiCall('/staff/profile/privacy', {
method: 'PUT',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-privacy'),
body: {
profileVisible: true,
},
});
logStep('staff.profile.privacy.update.ok', updatedPrivacy);
const appliedShift = await apiCall(`/staff/shifts/${openShift.shiftId}/apply`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-apply'),
body: {
roleId: openShift.roleId,
},
});
logStep('staff.shifts.apply.ok', appliedShift);
const acceptedShift = await apiCall(`/staff/shifts/${pendingShift.shiftId}/accept`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-accept'),
body: {},
});
logStep('staff.shifts.accept.ok', acceptedShift);
const clockIn = await apiCall('/staff/clock-in', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-clock-in'),
body: {
shiftId: clockableTodayShift.shiftId,
sourceType: 'GEO',
deviceId: 'smoke-iphone-15-pro',
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),
},
});
assert.equal(clockIn.validationStatus, 'FLAGGED');
assert.equal(clockIn.effectiveClockInMode, clockableTodayShift.clockInMode);
assert.equal(clockIn.overrideUsed, true);
assert.ok(clockIn.securityProofId);
logStep('staff.clock-in.ok', clockIn);
const duplicateClockIn = await apiCall('/staff/clock-in', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-clock-in-duplicate'),
body: {
shiftId: clockableTodayShift.shiftId,
sourceType: 'GEO',
deviceId: 'smoke-iphone-15-pro',
latitude: fixture.clockPoint.latitude,
longitude: fixture.clockPoint.longitude,
accuracyMeters: 8,
proofNonce: uniqueKey('geo-proof-clock-in-duplicate'),
proofTimestamp: isoTimestamp(0),
capturedAt: isoTimestamp(0),
},
expectedStatus: 409,
allowFailure: true,
});
assert.equal(duplicateClockIn.statusCode, 409);
assert.equal(duplicateClockIn.body.code, 'ALREADY_CLOCKED_IN');
logStep('staff.clock-in.duplicate.ok', duplicateClockIn.body);
const attendanceStatusAfterClockIn = await apiCall('/staff/clock-in/status', {
token: staffAuth.idToken,
});
logStep('staff.clock-in.status-after.ok', attendanceStatusAfterClockIn);
const locationStreamBatch = await apiCall('/staff/location-streams', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-location-stream'),
body: {
shiftId: clockableTodayShift.shiftId,
sourceType: 'GEO',
deviceId: 'smoke-iphone-15-pro',
points: [
{
capturedAt: isoTimestamp(0.05),
latitude: fixture.clockPoint.latitude,
longitude: fixture.clockPoint.longitude,
accuracyMeters: 12,
},
{
capturedAt: isoTimestamp(0.1),
latitude: fixture.clockPoint.latitude + 0.008,
longitude: fixture.clockPoint.longitude + 0.008,
accuracyMeters: 20,
},
{
capturedAt: isoTimestamp(0.15),
accuracyMeters: 25,
},
],
metadata: {
source: 'live-smoke-v2-unified',
},
},
});
assert.ok(locationStreamBatch.batchId);
assert.ok(locationStreamBatch.incidentIds.length >= 1);
logStep('staff.location-streams.ok', locationStreamBatch);
const coverageIncidentsAfter = await apiCall(`/client/coverage/incidents?${reportWindow}`, {
token: ownerSession.sessionToken,
});
assert.ok(coverageIncidentsAfter.items.length > coverageIncidentsBefore.items.length);
logStep('client.coverage.incidents-after.ok', { count: coverageIncidentsAfter.items.length });
const cancelledLateWorker = await apiCall(`/client/coverage/late-workers/${fixture.assignments.noShowAna.id}/cancel`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('client-late-worker-cancel'),
body: {
reason: 'Smoke cancellation for a confirmed late worker',
},
});
assert.equal(cancelledLateWorker.assignmentId, fixture.assignments.noShowAna.id);
assert.equal(cancelledLateWorker.status, 'CANCELLED');
assert.equal(cancelledLateWorker.replacementSearchTriggered, true);
logStep('client.coverage.late-worker-cancel.ok', cancelledLateWorker);
const clockOut = await apiCall('/staff/clock-out', {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-clock-out'),
body: {
shiftId: clockableTodayShift.shiftId,
sourceType: 'GEO',
deviceId: 'smoke-iphone-15-pro',
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 submittedCompletedShift = await apiCall(`/staff/shifts/${clockableTodayShift.shiftId}/submit-for-approval`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-submit-approval'),
body: {
note: 'Smoke approval submission',
},
});
assert.equal(submittedCompletedShift.submitted, true);
logStep('staff.shifts.submit-for-approval.ok', submittedCompletedShift);
const requestedSwap = await apiCall(`/staff/shifts/${fixture.shifts.swapEligible.id}/request-swap`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-swap'),
body: {
reason: 'Smoke swap request',
},
});
logStep('staff.shifts.request-swap.ok', requestedSwap);
const benOpenShifts = await apiCall('/staff/shifts/open?limit=10', {
token: staffBenAuth.idToken,
});
const benSwapShift = benOpenShifts.items.find((item) => item.shiftId === fixture.shifts.swapEligible.id);
assert.ok(benSwapShift);
assert.equal(benSwapShift.swapRequestId, requestedSwap.swapRequestId);
assert.equal(benSwapShift.dispatchTeam, 'CERTIFIED_LOCATION');
logStep('staff-b.shifts.open-swap.ok', benSwapShift);
const dispatchCandidates = await apiCall(`/client/coverage/dispatch-candidates?shiftId=${fixture.shifts.swapEligible.id}&roleId=${fixture.shiftRoles.swapEligibleBarista.id}`, {
token: ownerSession.sessionToken,
});
assert.ok(Array.isArray(dispatchCandidates.items));
assert.ok(dispatchCandidates.items.length >= 1);
assert.equal(dispatchCandidates.items[0].staffId, fixture.staff.ben.id);
logStep('client.coverage.dispatch-candidates.ok', { count: dispatchCandidates.items.length });
const benSwapApplication = await apiCall(`/staff/shifts/${fixture.shifts.swapEligible.id}/apply`, {
method: 'POST',
token: staffBenAuth.idToken,
idempotencyKey: uniqueKey('staff-b-shift-swap-apply'),
body: {
roleId: fixture.shiftRoles.swapEligibleBarista.id,
},
});
assert.ok(benSwapApplication.applicationId);
logStep('staff-b.shifts.apply-swap.ok', benSwapApplication);
const swapRequests = await apiCall('/client/coverage/swap-requests?status=OPEN', {
token: ownerSession.sessionToken,
});
const openSwapRequest = swapRequests.items.find((item) => item.swapRequestId === requestedSwap.swapRequestId);
assert.ok(openSwapRequest);
assert.ok(openSwapRequest.candidates.some((candidate) => candidate.staffId === fixture.staff.ben.id));
logStep('client.coverage.swap-requests.ok', { count: swapRequests.items.length });
const resolvedSwap = await apiCall(`/client/coverage/swap-requests/${requestedSwap.swapRequestId}/resolve`, {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('client-swap-resolve'),
body: {
applicationId: benSwapApplication.applicationId,
note: 'Smoke resolved swap request',
},
});
assert.equal(resolvedSwap.status, 'RESOLVED');
logStep('client.coverage.swap-resolve.ok', resolvedSwap);
const blockedReview = await apiCall('/client/coverage/reviews', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('coverage-block'),
body: {
staffId: fixture.staff.ana.id,
assignmentId: fixture.assignments.completedAna.id,
rating: 2,
markAsBlocked: true,
markAsFavorite: false,
issueFlags: ['LATE_CLOCK_IN'],
feedback: 'Smoke blocked staff test',
},
});
assert.equal(blockedReview.markAsBlocked, true);
logStep('client.coverage.block.ok', blockedReview);
const blockedStaff = await apiCall('/client/coverage/blocked-staff', {
token: ownerSession.sessionToken,
});
assert.ok(blockedStaff.items.some((item) => item.staffId === fixture.staff.ana.id));
logStep('client.coverage.blocked-staff.ok', { count: blockedStaff.items.length });
const blockedApplyAttempt = await apiCall(`/staff/shifts/${blockedApplyCandidate.shiftId}/apply`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-shift-apply-blocked'),
body: {
roleId: blockedApplyCandidate.roleId,
},
allowFailure: true,
});
assert.equal(blockedApplyAttempt.statusCode, 409);
assert.equal(blockedApplyAttempt.body?.code, 'STAFF_BLOCKED');
logStep('staff.shifts.apply-blocked.ok', blockedApplyAttempt.body);
const unblockedReview = await apiCall('/client/coverage/reviews', {
method: 'POST',
token: ownerSession.sessionToken,
idempotencyKey: uniqueKey('coverage-unblock'),
body: {
staffId: fixture.staff.ana.id,
assignmentId: fixture.assignments.completedAna.id,
rating: 5,
markAsBlocked: false,
markAsFavorite: true,
issueFlags: [],
feedback: 'Smoke unblock cleanup',
},
});
assert.equal(unblockedReview.markAsBlocked, false);
logStep('client.coverage.unblock.ok', unblockedReview);
const uploadedProfilePhoto = await uploadFile('/staff/profile/photo', staffAuth.idToken, {
filename: 'profile-photo.jpg',
contentType: 'image/jpeg',
content: Buffer.from('fake-profile-photo'),
});
assert.ok(uploadedProfilePhoto.fileUri);
logStep('staff.profile.photo.upload.ok', uploadedProfilePhoto);
const uploadedGovId = await finalizeVerifiedUpload({
token: staffAuth.idToken,
uploadCategory: 'staff-document',
filename: 'government-id.jpg',
contentType: 'image/jpeg',
content: Buffer.from('fake-government-id'),
finalizePath: `/staff/profile/documents/${fixture.documents.governmentId.id}/upload`,
finalizeMethod: 'PUT',
verificationType: 'government_id',
subjectId: fixture.documents.governmentId.id,
rules: {
documentId: fixture.documents.governmentId.id,
},
});
assert.equal(uploadedGovId.finalized.documentId, fixture.documents.governmentId.id);
logStep('staff.profile.document.upload.ok', uploadedGovId.finalized);
if (!['APPROVED', 'AUTO_PASS'].includes(`${uploadedGovId.finalized.verification?.status || ''}`)) {
const reviewedGovId = await approveVerification({
token: ownerSession.sessionToken,
verificationId: uploadedGovId.finalized.verification.verificationId,
note: 'Smoke approval for government ID',
});
assert.equal(reviewedGovId.status, 'APPROVED');
logStep('staff.profile.document.review.ok', {
verificationId: reviewedGovId.verificationId,
status: reviewedGovId.status,
});
}
const uploadedI9 = await finalizeVerifiedUpload({
token: staffAuth.idToken,
uploadCategory: 'staff-tax-form',
filename: 'i9-completed.pdf',
contentType: 'application/pdf',
content: Buffer.from('fake-i9-tax-form'),
finalizePath: `/staff/profile/documents/${fixture.documents.taxFormI9.id}/upload`,
finalizeMethod: 'PUT',
verificationType: 'tax_form',
subjectId: fixture.documents.taxFormI9.id,
rules: {
documentId: fixture.documents.taxFormI9.id,
formType: 'I-9',
},
});
assert.equal(uploadedI9.finalized.documentId, fixture.documents.taxFormI9.id);
logStep('staff.profile.tax-form.upload.ok', uploadedI9.finalized);
if (!['APPROVED', 'AUTO_PASS'].includes(`${uploadedI9.finalized.verification?.status || ''}`)) {
const reviewedI9 = await approveVerification({
token: ownerSession.sessionToken,
verificationId: uploadedI9.finalized.verification.verificationId,
note: 'Smoke approval for completed I-9',
});
assert.equal(reviewedI9.status, 'APPROVED');
logStep('staff.profile.tax-form.review.ok', {
verificationId: reviewedI9.verificationId,
status: reviewedI9.status,
});
}
const uploadedAttire = await finalizeVerifiedUpload({
token: staffAuth.idToken,
uploadCategory: 'staff-attire',
filename: 'black-shirt.jpg',
contentType: 'image/jpeg',
content: Buffer.from('fake-black-shirt'),
finalizePath: `/staff/profile/attire/${fixture.documents.attireBlackShirt.id}/upload`,
finalizeMethod: 'PUT',
verificationType: 'attire',
subjectId: fixture.documents.attireBlackShirt.id,
rules: {
dressCode: 'Black shirt',
},
});
assert.equal(uploadedAttire.finalized.documentId, fixture.documents.attireBlackShirt.id);
logStep('staff.profile.attire.upload.ok', uploadedAttire.finalized);
const certificateType = `ALCOHOL_SERVICE_${Date.now()}`;
const uploadedCertificate = await finalizeVerifiedUpload({
token: staffAuth.idToken,
uploadCategory: 'staff-certificate',
filename: 'certificate.pdf',
contentType: 'application/pdf',
content: Buffer.from('fake-certificate'),
finalizePath: '/staff/profile/certificates',
finalizeMethod: 'POST',
verificationType: 'certification',
subjectId: certificateType,
rules: {
certificateName: 'Alcohol Service Permit',
certificateIssuer: 'Demo Issuer',
},
finalizeBody: {
certificateType,
name: 'Alcohol Service Permit',
issuer: 'Demo Issuer',
},
});
assert.equal(uploadedCertificate.finalized.certificateType, certificateType);
logStep('staff.profile.certificate.upload.ok', uploadedCertificate.finalized);
const profileDocumentsAfter = await apiCall('/staff/profile/documents', {
token: staffAuth.idToken,
});
const governmentIdAfter = profileDocumentsAfter.items.find((item) => item.documentId === fixture.documents.governmentId.id);
assert.ok(governmentIdAfter);
assert.equal(governmentIdAfter.status, 'VERIFIED');
logStep('staff.profile.documents-after.ok', { count: profileDocumentsAfter.items.length });
const availableOrdersAfterVerification = await apiCall('/staff/orders/available?limit=20', {
token: staffAuth.idToken,
});
let eligibleOrder = null;
let eligibleOrderDetail = null;
for (const item of availableOrdersAfterVerification.items) {
const detail = await apiCall(`/staff/orders/${item.orderId}`, {
token: staffAuth.idToken,
});
if (detail.eligibility?.isEligible === true) {
eligibleOrder = item;
eligibleOrderDetail = detail;
break;
}
}
assert.ok(eligibleOrder, 'Expected at least one eligible available order after document verification');
const bookedOrder = await apiCall(`/staff/orders/${eligibleOrder.orderId}/book`, {
method: 'POST',
token: staffAuth.idToken,
idempotencyKey: uniqueKey('staff-order-book'),
body: {
roleId: eligibleOrderDetail.roleId,
},
});
assert.equal(bookedOrder.orderId, eligibleOrder.orderId);
assert.ok(bookedOrder.assignedShiftCount >= 1);
assert.equal(bookedOrder.status, 'PENDING');
assert.ok(Array.isArray(bookedOrder.assignedShifts));
logStep('staff.orders.book.ok', {
orderId: bookedOrder.orderId,
assignedShiftCount: bookedOrder.assignedShiftCount,
status: bookedOrder.status,
});
const pendingShiftsAfterBooking = await apiCall('/staff/shifts/pending', {
token: staffAuth.idToken,
});
assert.ok(
bookedOrder.assignedShifts.some((shift) => pendingShiftsAfterBooking.items.some((item) => item.shiftId === shift.shiftId))
);
logStep('staff.shifts.pending-after-order-book.ok', {
count: pendingShiftsAfterBooking.items.length,
bookedShiftCount: bookedOrder.assignedShiftCount,
});
const certificatesAfter = await apiCall('/staff/profile/certificates', {
token: staffAuth.idToken,
});
assert.ok(certificatesAfter.items.some((item) => item.certificateType === certificateType));
logStep('staff.profile.certificates-after.ok', { count: certificatesAfter.items.length });
const deletedCertificate = await apiCall(`/staff/profile/certificates/${certificateType}`, {
method: 'DELETE',
token: staffAuth.idToken,
expectedStatus: 200,
});
logStep('staff.profile.certificate.delete.ok', deletedCertificate);
const clientSignOut = await apiCall('/auth/client/sign-out', {
method: 'POST',
token: ownerSession.sessionToken,
});
logStep('auth.client.sign-out.ok', clientSignOut);
const staffSignOut = await apiCall('/auth/staff/sign-out', {
method: 'POST',
token: staffAuth.idToken,
});
logStep('auth.staff.sign-out.ok', staffSignOut);
// eslint-disable-next-line no-console
console.log('LIVE_SMOKE_V2_UNIFIED_OK');
}
main().catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
});