---
id: TST-PFM-MTD-118
title: custom fields
kind: integration
product_area: platform
module: metadata
revision: 1
status: draft
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
repository: services-platform
files:
  - modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js
implements:
  - ATS-PFM-MTD-005-02
  - ATS-PFM-MTD-005-03
  - ATS-PFM-MTD-005-04
  - ATS-PFM-MTD-005-05
  - ATS-PFM-MTD-005-06
  - ATS-PFM-MTD-005-07
  - ATS-PFM-MTD-005-10
  - ATS-PFM-MTD-005-11
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/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#answers
    the field identified by its object and key
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#answers
    the labels and the data type it was created with
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#fills
    in the default display of the data type
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stamps
    version 1 and the caller as the creator
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#answers
    an empty configuration for a data type that takes none
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#reads
    the persisted field back identical to what it answered
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    large text with a maximum length and its multi-line display
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    text with a maximum length
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    an e-mail address field, which takes no configuration
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a phone number field, which takes no configuration
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a web address field, which takes no configuration
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a currency with its currency code, places and exact decimal-string minimum
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a percent with its places and exact decimal-string maximum
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    HTML with its editor display
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    an integer with bounds and a select display
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    an integer with explicit choices
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a decimal with its places and exact decimal-string bounds
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a boolean with its true and false labels
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a file with its accepted media types
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    an image with no accepted media types
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a multiple-selection list with its option ids, labels and the archived one
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a date field
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a datetime field
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    a reference with its target object
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    an object that was never registered as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a retired object as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a standard key of the object
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    another standard key of the object
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a snake_case key without the custom suffix
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a key that is not lowercase
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a key with a double underscore before the suffix
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    an empty key
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a key the tenant already used on the object
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a key an archived field of the tenant still holds
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#lets
    another tenant use the same key: keys are unique per tenant and object
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    an invalid definition listing every problem at once
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a decimal without the configuration its data type requires
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a select display on an integer whose range is too large to pick from
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    it before the database is reached, naming the target
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#writes
    no row for the refused field
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    it, naming the configuration, because a custom field cannot hold one
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#writes
    no row for the refused field
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to act without a tenant in the execution context
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a tenant the caller cannot reach
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#never
    answers another tenant's field to a read
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#never
    answers this tenant's field to the other tenant's read
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#lists
    the tenant's own field
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#leaves
    the other tenant's field out of the list
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#lists
    the other tenant's own field to the other tenant alone
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to change another tenant's field as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to archive another tenant's field as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#leaves
    the other tenant's row untouched by both refusals
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#listed
    the field before it was archived
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#leaves
    an archived field out of the default list
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#includes
    an archived field when asked
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to list the fields of an object that was never registered
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#answers
    the new labels and display, keeping the object, key and data type
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#bumps
    the version to 2
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#persists
    the replacement, read back through the repository
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#relabels
    and archives options without changing their ids, and adds an option
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#persists
    the relabelled and archived options
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#widens
    a decimal to more places
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#persists
    the widened decimal
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to replace a field the tenant never had, as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    none for a field created without a level
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
    the level a create names
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#changes
    the level on a replace, read back through the repository
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#resets
    the level to none when a replace leaves it out
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a level outside the four, naming the property
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a change of data type, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    removing an option, saying to archive it instead, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a display the configuration does not support, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a stale version as StaleObject, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    narrowing a decimal to fewer places, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    an archived field until it is restored, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a field locked against updates, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stamps
    the moment of archival
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#bumps
    the version on archival
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#persists
    the archival, read back through the repository
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#clears
    the archival stamp on restore
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#keeps
    the object and key through archival and restore, and bumps the version again
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#persists
    the restore, read back through the repository
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to restore a field that is not archived
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to archive a field twice
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to archive a locked field, the row unchanged
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to archive a field the tenant never had, as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    to restore a field whose object has been retired, as a 404
  - >-
    services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#db/migrations/0010_metadata.sql
fixture_imports:
  - drizzle-orm
  - ../../../../db/custom-fields.table
  - ../../../../services/persistence/custom-fields
  - ../../../../services/registration/register-application
  - '@rhythmsoftware/macroservice-testing/execution-context'
  - '@rhythmsoftware/macroservice-testing/stage'
  - '@rhythmsoftware/macroservice-testing/refusals'
  - '@rhythmsoftware/macroservice-errors/errors'
  - '@rhythmsoftware/macroservice-errors/pg-errors'
  - ../../support
test_doubles: []
limitations:
  - Conditional or skipped cases remain outside any claimed passing execution.
unmatched_specification: null
binding_assertions:
  - scenario: ATS-PFM-MTD-005-02
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a key an archived field of the tenant still holds
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#lets
        another tenant use the same key: keys are unique per tenant and object
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a standard key of the object
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-03
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a change of data type, the row unchanged
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-04
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        removing an option, saying to archive it instead, the row unchanged
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        narrowing a decimal to fewer places, the row unchanged
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#widens
        a decimal to more places
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-05
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        an invalid definition listing every problem at once
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a decimal without the configuration its data type requires
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a select display on an integer whose range is too large to pick from
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-06
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#stores
        none for a field created without a level
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#resets
        the level to none when a replace leaves it out
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a level outside the four, naming the property
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-07
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        an archived field until it is restored, the row unchanged
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        to archive a field twice
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-10
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a retired object as a 404
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        it before the database is reached, naming the target
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        it, naming the configuration, because a custom field cannot hold one
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
  - scenario: ATS-PFM-MTD-005-11
    provenance: test
    source:
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        a field locked against updates, the row unchanged
      - >-
        services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
        to archive a locked field, the row unchanged
    note: >-
      These references identify the assertions this suite contributes; bindings to additional suites remain
      part of the scenario’s proof.
---

# Custom fields

## Verification Boundary

This integration suite checks record descriptions and field configuration 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. Conditional or skipped cases remain outside any claimed passing execution.

## 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.
