Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ This suite currently targets:
- society onboarding through root APIs
- admin authentication and protected endpoint access
- OpenAPI-driven billing/payment smoke coverage
- accounting and treasury operation flows
- staff attendance and payroll processing flows
- utility monitoring and marketplace transaction flows
- communication module lifecycle flows
- config, document repository, and analytics governance flows
- admin refresh token rotation (`/auth/refresh` and `/auth/refresh-token`)
- session invalidation on logout
- session invalidation on password change
Expand Down Expand Up @@ -48,11 +53,14 @@ ShieldGuard/
│ └── utils/
│ ├── containerDiagnostics.js
│ ├── dataFactory.js
│ ├── flowHarness.js
│ ├── openApiContract.js
│ ├── polling.js
│ ├── rootAuth.js
│ └── rootCredential.js
└── tests/
├── accounting/
│ └── accounting-treasury-flows.e2e.test.js
├── amenities-meeting/
│ └── amenities-meeting-flows.e2e.test.js
├── asset-complaint/
Expand All @@ -68,6 +76,14 @@ ShieldGuard/
│ └── tenant-onboarding.e2e.test.js
├── helpdesk-emergency/
│ └── helpdesk-emergency-flows.e2e.test.js
├── communication/
│ └── communication-flows.e2e.test.js
├── config-document-analytics/
│ └── config-document-analytics-flows.e2e.test.js
├── staff-payroll/
│ └── staff-payroll-flows.e2e.test.js
├── utility-marketplace/
│ └── utility-marketplace-flows.e2e.test.js
└── visitor/
└── visitor-gatepass-flows.e2e.test.js
```
Expand Down Expand Up @@ -169,6 +185,36 @@ Run only helpdesk and emergency lifecycle checks:
npm run test:e2e:helpdesk-emergency
```

Run only accounting and treasury lifecycle checks:

```bash
npm run test:e2e:accounting
```

Run only staff and payroll lifecycle checks:

```bash
npm run test:e2e:staff-payroll
```

Run only utility and marketplace lifecycle checks:

```bash
npm run test:e2e:utility-marketplace
```

Run only communication lifecycle checks:

```bash
npm run test:e2e:communication
```

Run only config, document, and analytics lifecycle checks:

```bash
npm run test:e2e:config-document-analytics
```

## CI Pipeline

GitHub Actions workflow: `.github/workflows/ci.yml`
Expand Down Expand Up @@ -302,6 +348,44 @@ Detailed interpretation is documented in `docs/DIAGNOSTICS_GUIDE.md`.
- `drives emergency alert and safety inspection workflows with rejection checks`
- Covers emergency contact creation, safety equipment/inspection creation, SOS raise/respond/resolve lifecycle, and invalid equipment rejection path.

### `accounting-treasury-flows.e2e.test.js`

- `drives account heads, funds, ledgers, expenses, and vendor payments lifecycle`
- Covers account head creation, fund categories, budgets, ledger entries, expense approval, vendor payment states, and financial reports.
- Verifies both modern (`/ledger-entries`) and legacy (`/ledger`) accounting surfaces in one flow.
- `rejects unauthenticated accounting writes and invalid resource lookups`
- Verifies protected write endpoints reject unauthenticated traffic.
- Verifies not-found semantics for invalid resource IDs.

### `staff-payroll-flows.e2e.test.js`

- `drives staff attendance to payroll generation, processing, and approval lifecycle`
- Covers staff onboarding, payroll components, salary structures, attendance check-in/out, payroll generation, processing, approval, and payslip retrieval.
- `drives staff leave approval and exports with access-control checks`
- Covers leave request to approval flow and leave balance checks.
- Verifies role-protected CSV export access behavior.

### `utility-marketplace-flows.e2e.test.js`

- `tracks complete utility lifecycle across water, electricity, and generator logs`
- Covers tank setup, water logs/charting, meter and reading logs, and diesel generator runtime summaries.
- `drives marketplace listing, inquiry, and carpool flow with ownership checks`
- Covers listing creation, inquiry flow, ownership-enforced sell transition, view incrementing, and carpool route discovery.

### `communication-flows.e2e.test.js`

- `drives announcement lifecycle with attachment, publish, read receipt, and statistics`
- Covers announcement creation, attachments, publish flow, resident read receipts, and admin statistics access.
- `drives polls, newsletters, notifications, and preference updates lifecycle`
- Covers poll vote lifecycle, newsletter publish/archive-by-year, notification read semantics, and preference updates.

### `config-document-analytics-flows.e2e.test.js`

- `drives tenant config, module settings, and document repository lifecycle`
- Covers tenant config upsert/bulk update, module toggles, billing formula config, and document category/document lifecycle.
- `drives report templates, scheduled reports, dashboards, and analytics endpoints`
- Covers report template execution, scheduled report send-now, dashboard defaulting, and core analytics endpoint verification with seeded operational data.

## Notes

- If root credential in `root-bootstrap-credential.txt` is stale, set `SHIELD_ROOT_PASSWORD` in `.env`.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
"test:e2e:auth-otp-lockout": "jest --runInBand --config jest.config.cjs tests/auth/otp-lockout.e2e.test.js",
"test:e2e:amenities-meeting": "jest --runInBand --config jest.config.cjs tests/amenities-meeting/amenities-meeting-flows.e2e.test.js",
"test:e2e:helpdesk-emergency": "jest --runInBand --config jest.config.cjs tests/helpdesk-emergency/helpdesk-emergency-flows.e2e.test.js",
"test:e2e:accounting": "jest --runInBand --config jest.config.cjs tests/accounting/accounting-treasury-flows.e2e.test.js",
"test:e2e:billing": "jest --runInBand --config jest.config.cjs tests/billing/billing-payments-contract-smoke.e2e.test.js",
"test:e2e:asset-complaint": "jest --runInBand --config jest.config.cjs tests/asset-complaint/asset-complaint-workflows.e2e.test.js",
"test:e2e:visitor": "jest --runInBand --config jest.config.cjs tests/visitor/visitor-gatepass-flows.e2e.test.js",
"test:e2e:staff-payroll": "jest --runInBand --config jest.config.cjs tests/staff-payroll/staff-payroll-flows.e2e.test.js",
"test:e2e:utility-marketplace": "jest --runInBand --config jest.config.cjs tests/utility-marketplace/utility-marketplace-flows.e2e.test.js",
"test:e2e:communication": "jest --runInBand --config jest.config.cjs tests/communication/communication-flows.e2e.test.js",
"test:e2e:config-document-analytics": "jest --runInBand --config jest.config.cjs tests/config-document-analytics/config-document-analytics-flows.e2e.test.js",
"test:e2e:watch": "jest --watch --config jest.config.cjs",
"shield:start": "node scripts/shield-runtime.cjs start",
"shield:stop": "node scripts/shield-runtime.cjs stop",
Expand Down
8 changes: 8 additions & 0 deletions src/clients/shieldApiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class ShieldApiClient {

return request.send(body || {});
}

delete(path, accessToken) {
let request = this.client.delete(path).set('Accept', 'application/json');
if (accessToken) {
request = request.set('Authorization', `Bearer ${accessToken}`);
}
return request;
}
}

module.exports = {
Expand Down
77 changes: 77 additions & 0 deletions src/utils/flowHarness.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const { createStrongPassword, randomSuffix } = require('./dataFactory')
const { loginWithEmail, onboardSocietyWithAdmin } = require('./onboarding')

function ensureExpectedStatus(response, expectedStatuses, method, path) {
if (expectedStatuses.includes(response.status)) {
return
}

throw new Error(
`${method} ${path} expected ${expectedStatuses.join(' or ')}, got ${response.status} (${response.body?.message || 'no message'})`
)
}

async function resolveAdminSession(suite, context, scenarioTag) {
try {
if (suite.config.adminEmail && suite.config.adminPassword) {
context.adminSession = await loginWithEmail(suite.api, suite.config.adminEmail, suite.config.adminPassword)
return
}

const onboarding = await onboardSocietyWithAdmin(suite.api, suite.config)
if (onboarding.onboardingBlocked) {
context.setupBlockedReason =
`${onboarding.onboardingBlockedReason || `${scenarioTag} onboarding unavailable.`} ` +
`Set SHIELD_ADMIN_EMAIL and SHIELD_ADMIN_PASSWORD in ShieldGuard/.env for ${scenarioTag}.`
return
}

context.adminSession = onboarding.adminSession
} catch (error) {
const message = error?.message || `${scenarioTag} setup failed`
context.setupBlockedReason =
`${message}. Set SHIELD_ROOT_PASSWORD or provide SHIELD_ADMIN_EMAIL and SHIELD_ADMIN_PASSWORD in ShieldGuard/.env.`
}
}

function skipIfSetupBlocked(context) {
if (!context.setupBlockedReason) {
return false
}

expect(context.setupBlockedReason).toMatch(/SHIELD_(ADMIN_EMAIL|ADMIN_PASSWORD|ROOT_PASSWORD)/)
return true
}

async function createUser(suite, accessToken, unitId, role, namePrefix) {
const suffix = randomSuffix().replace(/[^0-9]/g, '')
const password = createStrongPassword(role)
const response = await suite.api.post(
'/api/v1/users',
{
unitId,
name: `${namePrefix} ${suffix}`,
email: `${role.toLowerCase()}.${namePrefix.toLowerCase()}.${suffix}@shieldguard.test`,
phone: `+9166${suffix.slice(-8)}`,
password,
role
},
accessToken
)
ensureExpectedStatus(response, [200], 'POST', '/api/v1/users')

return {
user: response.body.data,
credentials: {
email: response.body.data.email,
password
}
}
}

module.exports = {
ensureExpectedStatus,
resolveAdminSession,
skipIfSetupBlocked,
createUser
}
Loading
Loading