Skip to content

The test block (test) ​

In traditional development, you write tests to ensure you didn't make a mistake. In Carotene, you write tests to ensure the AI doesn't make a mistake.

Because Carotene uses the @(...) Generative Operator to let the AI write your complex business logic, testing is no longer an afterthought—it is the steering wheel. The test block acts as an unbreakable mathematical contract. If the AI hallucinates the wrong math, the test fails, the build stops, and the compiler forces the AI to fix it autonomously.

1. Auto-Generated Structural Tests (Zero-Code) ​

You do not need to write tests to verify your Carotene architecture.

Because Carotene is a strictly typed blueprint, the compiler automatically generates and executes a comprehensive structural test suite based entirely on your .carrot files. Without you writing a single line of test code, Carotene automatically performs:

  • Type Fuzzing (Input/Output): The test runner bombards every function with edge-case data (nulls, max integers, empty strings) to guarantee it never crashes unexpectedly and that the output perfectly matches the expected Future<Type>.
  • Schema Validation: Ensures bad data correctly returns a 400 Bad Request.
  • RBAC (requires): Proves unauthorized users get 401 or 403 errors.
  • State Machines (flow): Proves illegal lifecycle transitions are strictly blocked.
  • Zero-Trust Sandbox: Proves functions cannot mutate tables without explicit updates or creates verbs.

If the architecture dictates it, the compiler automatically tests it.

2. Business Logic Tests (The test primitive) ​

If the compiler handles the architecture, you only need to write Business Logic Tests. You need to verify the specific outputs of your @(...) Generative Operators and deterministic rules.

The Testing Keywords ​

Carotene eliminates nested boilerplate and relies on a flat test block with standard code execution and three specialized keywords:

  • given: Seeds the isolated memory sandbox with starting data. You can optionally bind this to a local variable to pass directly into your functions.
  • mocks: Intercepts a network boundary. If your function calls an external API, mocks prevents the request from hitting the real internet and instantly returns a designated fake value.
  • asserts: Verifies the final state of the database, the returned values, or network side-effects.

Example: Testing the Generative Operator ​

Let's write a test for our ProcessRefund function. We need to guarantee that the AI correctly interprets our prompt: @("Calculate the prorated refund amount...") and applies the deterministic rules (like the Gold Tier fee waiver).

dart
domain Commerce {
  
  // Scenario A: Standard Customer (Pays the $5 fee)
  test "Calculates prorated refund and deducts standard fee" {
    
    // 1. Sandboxed Data Construction 
    given store.Customer { id: "cust_1", loyaltyTier: "Standard" }
    given store.Order { 
      id: "ord_1", 
      customerId: customer.id, 
      items: [ { price: 100.0, returned: true } ]
    }
    
    // 2. Auto-Mock External Side-Effects
    mocks integration.Stripe.RefundPayment -> true

    // 3. Trigger the action (Standard function call)
    ProcessRefund("ord_1")

    // 4. Verify the AI's `@(...)` math and side-effects
    asserts store.Order("ord_1").status == Refunded
    
    // The item was $100, minus the $5 fee. If the AI returns $100, the build fails.
    asserts integration.Stripe.RefundPayment.wasCalledWith(95.0)
  }

  // Scenario B: Gold Customer (Fee is waived)
  test "Waives fee for Gold Tier customers" {
    
    given store.Customer { id: "cust_2", loyaltyTier: "Gold" }
    given store.Order { 
      id: "ord_2", 
      customerId: goldCustomer.id, 
      items: [ { price: 100.0, returned: true } ] 
    }
    
    mocks integration.Stripe.RefundPayment -> true

    ProcessRefund("ord_2")

    // The $5 deterministic fee was waived to $0. Total refund should be $100.
    asserts integration.Stripe.RefundPayment.wasCalledWith(100.0)
  }
}

3. Test-Driven Generation (TDG) ​

When you write a test block, you are engaging in Test-Driven Generation.

When you run carrot build, Carotene initiates an autonomous feedback loop:

  1. Generate: The AI reads your @(...) operators and writes the underlying TypeScript/Go implementation.
  2. Execute: The compiler runs the AI's generated code against your test blocks in a completely isolated, instantly-vaporized memory sandbox.
  3. Critique: If an asserts statement fails (e.g., the AI forgot to subtract the $5 fee, returning 100.0 instead of 95.0), the compiler intercepts the failure and feeds the stack trace back to the AI.
  4. Refine: The AI autonomously rewrites the logic and tries again.

The compilation only finishes when all your tests pass green. By writing tests in Carotene, you are mathematically guaranteeing that the generated code perfectly matches your exact business requirements before a single line ever reaches production.