new test contracts
This commit is contained in:
@@ -5,12 +5,75 @@
|
||||
# @LAYER: Domain (Core)
|
||||
# @RELATION: DEPENDS_ON -> [DEF:Infra:PostgresDB]
|
||||
# @RELATION: DEPENDS_ON -> [DEF:Infra:AuditLog]
|
||||
#
|
||||
# @INVARIANT: Total system balance must remain constant (Double-Entry Bookkeeping).
|
||||
# @INVARIANT: Negative transfers are strictly forbidden.
|
||||
# @INVARIANT: No partial commit must occur under failure (ACID Atomicity).
|
||||
|
||||
# @TEST_CONTRACT: TransferInput ->
|
||||
# {
|
||||
# required_fields: {
|
||||
# sender_id: str,
|
||||
# receiver_id: str,
|
||||
# amount: Decimal
|
||||
# },
|
||||
# invariants: [
|
||||
# "amount > 0",
|
||||
# "sender_id != receiver_id"
|
||||
# ],
|
||||
# constraints: [
|
||||
# "sender must exist",
|
||||
# "receiver must exist"
|
||||
# ]
|
||||
# }
|
||||
|
||||
# @TEST_CONTRACT: TransferResult ->
|
||||
# {
|
||||
# required_fields: {
|
||||
# tx_id: str,
|
||||
# status: str,
|
||||
# new_balance: Decimal
|
||||
# },
|
||||
# invariants: [
|
||||
# "status == COMPLETED implies balance mutation occurred"
|
||||
# ]
|
||||
# }
|
||||
|
||||
# @TEST_FIXTURE: sufficient_funds ->
|
||||
# {
|
||||
# sender_balance: 500.00,
|
||||
# receiver_balance: 100.00,
|
||||
# amount: 100.00
|
||||
# }
|
||||
|
||||
# @TEST_EDGE: insufficient_funds ->
|
||||
# {
|
||||
# sender_balance: 50.00,
|
||||
# receiver_balance: 100.00,
|
||||
# amount: 100.00
|
||||
# }
|
||||
#
|
||||
# @TEST_EDGE: negative_amount ->
|
||||
# {
|
||||
# sender_balance: 500.00,
|
||||
# receiver_balance: 100.00,
|
||||
# amount: -10.00
|
||||
# }
|
||||
#
|
||||
# @TEST_EDGE: self_transfer ->
|
||||
# {
|
||||
# sender_id: "acc_A",
|
||||
# receiver_id: "acc_A",
|
||||
# amount: 10.00
|
||||
# }
|
||||
|
||||
# @TEST_EDGE: audit_failure -> raises Exception
|
||||
# @TEST_EDGE: concurrency_conflict -> special: concurrent_execution
|
||||
|
||||
# @TEST_INVARIANT: total_balance_constant -> verifies: [sufficient_funds, concurrency_conflict]
|
||||
# @TEST_INVARIANT: no_partial_commit -> verifies: [audit_failure]
|
||||
# @TEST_INVARIANT: negative_transfer_forbidden -> verifies: [negative_amount]
|
||||
|
||||
# @TEST_DATA: sufficient_funds -> {"from": "acc_A", "to": "acc_B", "amt": 100.00}
|
||||
# @TEST_DATA: insufficient_funds -> {"from": "acc_empty", "to": "acc_B", "amt": 1000.00}
|
||||
# @TEST_DATA: concurrency_lock -> {./fixtures/transactions.json#race_condition}
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import NamedTuple
|
||||
|
||||
@@ -1,24 +1,67 @@
|
||||
<!-- [DEF:FrontendComponentShot:Component] -->
|
||||
<!-- /**
|
||||
* @TIER: CRITICAL
|
||||
* @SEMANTICS: Task, Button, Action, UX
|
||||
* @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
|
||||
* @LAYER: UI (Presentation)
|
||||
* @RELATION: CALLS -> postApi
|
||||
* @INVARIANT: Must prevent double-submission while loading.
|
||||
*
|
||||
* @TEST_DATA: idle_state -> {"isLoading": false}
|
||||
* @TEST_DATA: loading_state -> {"isLoading": true}
|
||||
*
|
||||
* @UX_STATE: Idle -> Button enabled, primary color.
|
||||
* @UX_STATE: Loading -> Button disabled, spinner visible.
|
||||
* @UX_STATE: Error -> Toast notification triggers.
|
||||
*
|
||||
* @UX_FEEDBACK: Toast success/error.
|
||||
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
|
||||
* @UX_TEST: Success -> {api_resolve: 200, expected: toast.success called}
|
||||
*/
|
||||
-->
|
||||
<!--
|
||||
/**
|
||||
* @TIER: CRITICAL
|
||||
* @SEMANTICS: Task, Button, Action, UX
|
||||
* @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
|
||||
* @LAYER: UI (Presentation)
|
||||
* @RELATION: CALLS -> postApi
|
||||
*
|
||||
* @INVARIANT: Must prevent double-submission while loading.
|
||||
* @INVARIANT: Loading state must always terminate (no infinite spinner).
|
||||
* @INVARIANT: User must receive feedback on both success and failure.
|
||||
*
|
||||
* @TEST_CONTRACT: ComponentState ->
|
||||
* {
|
||||
* required_fields: {
|
||||
* isLoading: bool
|
||||
* },
|
||||
* invariants: [
|
||||
* "isLoading=true implies button.disabled=true",
|
||||
* "isLoading=true implies aria-busy=true",
|
||||
* "isLoading=true implies spinner visible"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @TEST_CONTRACT: ApiResponse ->
|
||||
* {
|
||||
* required_fields: {},
|
||||
* optional_fields: {
|
||||
* task_id: str
|
||||
* }
|
||||
* }
|
||||
|
||||
* @TEST_FIXTURE: idle_state ->
|
||||
* {
|
||||
* isLoading: false
|
||||
* }
|
||||
*
|
||||
* @TEST_FIXTURE: successful_response ->
|
||||
* {
|
||||
* task_id: "task_123"
|
||||
* }
|
||||
|
||||
* @TEST_EDGE: api_failure -> raises Error("Network")
|
||||
* @TEST_EDGE: empty_response -> {}
|
||||
* @TEST_EDGE: rapid_double_click -> special: concurrent_click
|
||||
* @TEST_EDGE: unresolved_promise -> special: pending_state
|
||||
|
||||
* @TEST_INVARIANT: prevent_double_submission -> verifies: [rapid_double_click]
|
||||
* @TEST_INVARIANT: loading_state_consistency -> verifies: [idle_state, pending_state]
|
||||
* @TEST_INVARIANT: feedback_always_emitted -> verifies: [successful_response, api_failure]
|
||||
|
||||
* @UX_STATE: Idle -> Button enabled, primary color, no spinner.
|
||||
* @UX_STATE: Loading -> Button disabled, spinner visible, aria-busy=true.
|
||||
* @UX_STATE: Success -> Toast success displayed.
|
||||
* @UX_STATE: Error -> Toast error displayed.
|
||||
*
|
||||
* @UX_FEEDBACK: toast.success, toast.error
|
||||
*
|
||||
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
|
||||
* @UX_TEST: Loading -> {double_click: ignored, expected: single_api_call}
|
||||
* @UX_TEST: Success -> {api_resolve: task_id, expected: toast.success called}
|
||||
* @UX_TEST: Error -> {api_reject: error, expected: toast.error called}
|
||||
-->
|
||||
<script>
|
||||
import { postApi } from "$lib/api.js";
|
||||
import { t } from "$lib/i18n";
|
||||
|
||||
Reference in New Issue
Block a user