Skip to content

Testing Strategy

Testing Goals

The Universal Entity Platform has important invariants. Tests should protect those invariants.

Focus testing on:

  • hierarchy correctness,
  • path correctness,
  • authorization scope,
  • soft delete behavior,
  • property validation,
  • tag selection rules,
  • group membership rules,
  • relationship rules,
  • transaction safety.

Test Types

Use multiple test levels.

text
Unit tests
Integration tests
Contract tests
End-to-end tests
Migration tests

Unit Tests

Unit test pure functions.

Examples:

  • path label generation,
  • value validation,
  • capability checks,
  • DTO mappers,
  • error mapping,
  • template expansion logic.

Integration Tests

Integration tests should run against real PostgreSQL.

Do not mock ltree behavior with SQLite.

Test:

  • create root entity,
  • create child entity,
  • get children,
  • get descendants,
  • get ancestors,
  • get path,
  • move subtree,
  • reject cycle,
  • reject out-of-scope access,
  • soft-delete subtree,
  • restore subtree.

Contract Tests

Contract tests should ensure frontend and backend agree on:

  • request schemas,
  • response schemas,
  • error codes,
  • DTO shapes.

If using Zod contracts, backend route handlers should parse inputs with the shared schema.

End-To-End Tests

E2E tests should cover key user workflows:

  • create customer,
  • create site under customer,
  • create building under site,
  • create asset under building,
  • edit asset properties,
  • assign tags,
  • move asset,
  • soft-delete asset,
  • restore asset.

Migration Tests

Migration tests should verify:

  • extensions are enabled,
  • indexes exist,
  • constraints exist,
  • seed data loads,
  • rollback strategy is understood.

Invariant Test Matrix

InvariantTest
Entity type must existcreating entity with invalid type fails
Parent must existcreating child under missing parent fails
Path must include parent pathchild path equals parent path + label
No cyclesmoving parent under descendant fails
Children are fast lookupchild query uses parent id
Descendants use ltreesubtree query returns expected nodes
Root scope appliesuser cannot edit outside root
Soft delete cascades logicallysubtree records get deleted_at
Relationships exclude containmentcontains relationship rejected
Single-select tag enforcedsecond active tag in same group replaces or rejects
Sensitive property protectedplaintext secret value rejected

Example Test Dataset

text
Customer A
├── Site A1
│   ├── Building A1-B1
│   │   ├── Asset A
│   │   └── Asset B
│   └── Building A1-B2
└── Site A2

Customer B
└── Site B1

Use this dataset for hierarchy, root-scope, and move tests.

Reparent Tests

Test valid move:

text
Move Asset A from Building A1-B1 to Building A1-B2

Expected:

  • parent changes,
  • path changes,
  • descendants change if any,
  • old parent children invalidated,
  • new parent children invalidated,
  • audit event emitted.

Test invalid move:

text
Move Building A1-B1 under Asset A

Expected:

text
CYCLE_DETECTED

Authorization Tests

Given:

text
User scoped to Customer A

Allowed:

text
edit Site A1
edit Asset A
read Building A1-B2

Denied:

text
read Customer B
edit Site B1
create asset under Customer B
move Asset A to Customer B

Soft Delete Tests

Soft-delete Building A1-B1.

Expected deleted:

  • Building A1-B1,
  • Asset A,
  • Asset B,
  • their property values,
  • tag assignments,
  • relationships touching them.

Expected not deleted:

  • Site A1,
  • Building A1-B2,
  • Customer A,
  • tags themselves,
  • property definitions.

Property Validation Tests

Test each value type.

Examples:

text
ip_address rejects invalid IP
integer rejects decimal
single_select rejects unknown option
secret_reference rejects plaintext
entity_reference rejects out-of-scope entity

Performance Tests

For large datasets, test:

  • descendant query time,
  • ancestor query time,
  • child lookup time,
  • move subtree time,
  • soft-delete subtree time.

Use EXPLAIN ANALYZE to confirm indexes are used.

Testing Rule

Any bug involving hierarchy, authorization, soft delete, or sensitive data should result in a regression test.