Skip to main content

Test chart storage

Test ChartStorageAdapter through the public chart controller against a production-shaped backend. A chart-storage integration is complete only when each enabled object survives a clean reload and cannot cross an unauthorized user or workspace boundary.

Start with capabilities

Inspect the mounted chart before testing UI:

const capabilities = widget.chart().getStorageCapabilities();
console.table(capabilities);

Every true value must correspond to a callable adapter method. Unsupported methods must stay absent. Feature settings may hide a supported control, but they must not imply backend support.

Run the chart-storage contract matrix

Object familyRequired success pathMissing resultAdditional failure proof
Chart workspace layoutCreate, list, load, update the same backend id, deleteloadLayout returns nullCreate without an assigned id fails; update does not duplicate
DrawingsSave, load, delete through tombstones, save a new revisionloadDrawings returns nullStale baseRevision preserves local and remote state
TemplateSave, list, load, apply, overwrite, deleteloadTemplate returns nullDrawing templates remain isolated by tool and id
User settingsLoad before ready, change, autosave, reload, delete keysloadUserSettings returns nullOverlapping saves cannot restore an older value
Replay stateSave cursor and speed, reload paused at the same cursorloadReplayState returns nullInvalid bar indices reject without changing live state
Chart image uploadUpload, save rewritten parent JSON, restore from the hosted URLNot applicableUpload failure prevents the parent JSON save

Use millisecond timestamps throughout. Ten-digit Unix-second values make chart workspace layout sorting and freshness displays incorrect.

Prove chart workspace layout identity

Exercise the canonical chart workspace controller:

  1. saveAs('opening-drive', 'Opening drive') creates a record and returns a logical layoutId plus a backend storageId.
  2. list() returns the saved chart workspace layout record.
  3. Change the symbol, interval, indicator stack, and drawing set, then load the chart workspace layout and verify the complete workspace returns.
  4. Save again and confirm the backend updates the original storageId.
  5. Reload the application, rebuild or restore the logical-to-storage mapping, repeat the update, and confirm no duplicate record appears.
  6. Delete the chart workspace layout and confirm both list and load report absence.

Run the same sequence for a single chart and a multi-chart workspace. A chart-level saveLayout() test cannot prove multi-chart workspace persistence. In the multi-chart case, remount with the same stable chart ids and confirm each saved entry reaches its matching controller. Also load a saved entry whose chart id is not mounted and confirm it is skipped rather than silently applied to a different chart.

Prove isolation and authorization

For every enabled chart-storage object family, test these changes independently:

  • user A to user B;
  • workspace A to workspace B;
  • one chart workspace layout or chart id to another;
  • one drawing sharing mode to another;
  • one drawing-template tool to another.

An unauthorized request must return a permission failure, never another tenant's data and never null disguised as absence. Repeat with an expired credential and a valid credential lacking the required scope.

Prove drawing concurrency

  1. Load the same separate drawing bucket in two clients.
  2. Save from client A and retain the returned revision.
  3. Save the stale copy from client B with its old baseRevision.
  4. Confirm the backend returns a conflict containing remoteRevision and the current remoteState.
  5. Resolve separate conflicts with resolveDrawingSharingConflict('reload-server') and resolveDrawingSharingConflict('keep-mine'); verify the server copy wins in the first case and the local copy is force-saved in the second.
  6. Separately call loadDrawings({ applicationMode: 'merge' }) with non-overlapping edits and confirm both sides remain. Repeat with edits to the same drawing and confirm they enter conflict state instead of being treated as a third resolution choice.
  7. Confirm a pushed update, when supported, reaches only the matching bucket.

For a custom subscribeDrawings, count open sockets, timers, and listeners. After a chart context change and after chart destruction, every count must return to zero and later server events must invoke no callback.

Verify chart image uploads

  1. Add an image drawing whose source is a local data: URL.
  2. Save each supported parent: a chart workspace layout, a separate drawing record, and a drawing template.
  3. Confirm the file service receives the upload before the parent JSON save.
  4. Confirm saved JSON contains the hosted URL and no inline image bytes.
  5. Reload in a clean browser session and verify the image renders.
  6. Reject an invalid or oversized upload and confirm no parent JSON is saved.

Exercise transport failures

Test the selected chart storage adapter with:

  • an empty backend and a genuine missing record;
  • malformed JSON and a structurally invalid public object;
  • 401, 403, 409, 429, and 5xx responses;
  • a successful empty delete response;
  • connection loss before a response and after the server may have written;
  • slow responses arriving after chart context has changed.

The built-in REST adapter does not add retries, timeouts, or cancellation. If a product requires them, wrap fetch and define whether an uncertain write is reconciled before retrying.

Finish with a browser smoke test

  • Mount with the same chart-storage and feature settings used in production.
  • Save and load chart state through the built-in storage UI, not only through direct adapter calls.
  • Confirm only supported rows are visible and denied permissions remain clear.
  • Reload in a clean browser context and restore each production object family.
  • Check narrow desktop and mobile widths for readable dialogs and keyboard focus order.
  • Destroy the chart and verify no chart-storage request, autosave, or subscription fires afterward.

Troubleshoot by boundary

SymptomFirst check
Chart-storage row is missinggetStorageCapabilities() and the matching feature setting
Every chart workspace layout save creates a duplicateCreate response id and retained ref.storageId mapping
Chart state disappears after reloadAdapter scope, browser storage key, or in-memory fallback
One tenant sees another tenant's dataServer-side identity derivation and database key
Drawings reappear after deletionremoved and removedGroups tombstones
Replay state works locally but not through RESTGET and POST /v1/replay, the layout wire query, and the returned ReplayState
An image restores as a broken URLUpload authorization, URL lifetime, and saved JSON rewrite

Next steps