---
id: TST-PFM-MTD-060
title: custom apps
kind: integration
product_area: platform
module: metadata
revision: 1
status: draft
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
repository: services-platform
files:
  - modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js
implements:
  - ATS-PFM-MTD-010-03
  - ATS-PFM-MTD-010-04
  - ATS-PFM-MTD-010-05
environment: local
implementation_state: implemented
latest_result: not-run
latest_run_at: null
latest_run_ref: null
conformance: not-verified
estimated_minutes: 5
isolation: >-
  Prepared integration database and the suite fixture records; fixture teardown restores or removes its own
  rows.
evidence:
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#creates
    an unarchived app at version 1 with the fields it stated
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#records
    the create in the security trail, with the fields it stated by name
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a key that is not a configuration id, and writes nothing
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    the key of a platform application, which a placement could not tell from it
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    the key of a retired platform application too
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a console app’s key that is not a licensing key (meetings), as the key of a platform application
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a missing label as is required
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    an icon that is not an icon key
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a display order below zero
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a key the tenant already has
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#lets
    another tenant have the same key
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#replaces
    the changeable fields and moves the version on
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#records
    the replacement in the trail with the names of the fields that changed
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a replacement at a stale version, the row unchanged
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a replacement of an app the tenant does not have
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a replacement without a label, the row unchanged
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#archives
    an app and records it in the trail
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    to archive an app twice, the row unchanged
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    a replacement of an archived app, which must be restored first
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#restores
    an archived app
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    to restore an app that is not archived
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    to change an app of another tenant, as not found
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#accepts
    the tenant’s twenty-first app and lists all twenty-one (Andrew’s B2: no limit until the app-wide limits
    mechanism)
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
    to archive an app while a draft and an active object are placed in it, naming both, and leaves the app
    unchanged
  - >-
    services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#archives
    the app once its objects are moved elsewhere or archived; an archived object keeps its placement
fixture_imports:
  - drizzle-orm
  - '@rhythmsoftware/macroservice-testing/refusals'
  - '@rhythmsoftware/macroservice-errors/errors'
  - ../../../../../security/db/security-audit-events.table
  - ../../../../db/custom-apps.table
  - ../../../../services/persistence/custom-apps
  - ../../support
  - ../../../../services/persistence/custom-objects
test_doubles: []
limitations: []
unmatched_specification: null
binding_assertions:
  - scenario: ATS-PFM-MTD-010-03
    provenance: test
    source:
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
        the key of a platform application, which a placement could not tell from it
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
        a console app’s key that is not a licensing key (meetings), as the key of a platform application
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
        a display order below zero
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-010-04
    provenance: test
    source:
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
        to archive an app while a draft and an active object are placed in it, naming both, and leaves the app
        unchanged
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#archives
        the app once its objects are moved elsewhere or archived; an archived object keeps its placement
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-010-05
    provenance: test
    source:
      - >-
        services-platform:modules/custom-objects/tests/integration/services/persistence/custom-apps.integration.spec.js#refuses
        a replacement of an archived app, which must be restored first
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
---

# Custom apps

## Verification Boundary

This integration suite checks custom objects and records within the local test harness. The registered file and pinned revision define its boundary.

## What This Suite Proves

The bindings identify the detailed or feature scenarios whose outcomes have assertions in this suite. Original case references remain attached to the detailed scenarios so a reviewer can inspect the exact assertion. Assertions are evidence of implemented checks; no execution result is claimed.

## What It Does Not Prove

The register does not establish a current passing run or full specification conformance. Behavior outside the suite’s fixtures and assertions remains unverified.

## Real Dependencies and Test Doubles

The fixture uses PostgreSQL and the imported persistence or API code. The fixture imports identify the setup seams; this register makes no broader claim that dependencies are undoubled.

## Must Not Be Doubled

The operation under examination and its asserted stored outcomes must remain real within the declared harness. A fixture cannot replace the write, refusal or transactional effect that the bound scenario checks.
