# Testing Policies & Oracles \[Unit-test Newton Rego policies with opa test, test WASM data oracles locally and via the Gateway, and run end-to-end policy simulations before deploying.]

Test at every layer before you deploy: unit-test your Rego rules with `opa test`, run your WASM oracle in isolation, then simulate the full policy end-to-end against the Newton network. This guide covers all three. For diagnosing failures after deployment, see [Testing & Debugging](/developers/resources/testing-debugging).

:::warning
Newton evaluates Rego v1 with its fork of Regorus 0.9.0, using an OPA v1.2.0-compatible subset plus Newton-specific extensions. Passing `opa test` validates policy logic against OPA, but it does not prove that every built-in used by the policy exists in Newton. Run the CLI Regorus checks and an end-to-end Newton simulation before deployment.
:::

## Unit-test your Rego with `opa test`

Rego rules are pure functions of `input`, `data.params`, and `data.wasm`, which makes them easy to unit-test without the network. Write a companion `policy_test.rego` and mock the data with `with`.

Given a policy like this:

```rego
package vault_risk_rating

import future.keywords

default allow := false

t := data.params

# Each oracle's output is namespaced under its pack id.
v := data.wasm.vaultsfyi

deny contains "apy_spike" if v.apy_z_score > t.apy_z_max

deny contains "risk_score_below_floor" if {
    v.risk_score != null
    v.risk_score < t.risk_score_floor
}

allow if count(deny) == 0
```

Write `policy_test.rego` alongside it. Mock `data.params` and `data.wasm` with `with`, and assert on both `allow` and membership in the `deny` set:

```rego
package vault_risk_rating_test

import data.vault_risk_rating

default_params := {
    "apy_z_max": 4,
    "risk_score_floor": 60,
}

clean_data := {
    "apy_z_score": 0.5,
    "risk_score": 90,
}

# The policy reads data.wasm.vaultsfyi.*, so wrap fixtures under the pack id.
wrap(inner) := {"vaultsfyi": inner}

with_data(overrides) := wrap(object.union(clean_data, overrides))

test_allow_when_all_clean if {
    d := wrap(clean_data)
    vault_risk_rating.allow with data.params as default_params with data.wasm as d
}

test_deny_apy_spike if {
    d := with_data({"apy_z_score": 5})
    "apy_spike" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
    not vault_risk_rating.allow with data.params as default_params with data.wasm as d
}

test_deny_risk_score_below_floor if {
    d := with_data({"risk_score": 30})
    "risk_score_below_floor" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
}

# A null risk score must not deny — guards against a fail-closed regression.
test_risk_score_null_does_not_deny if {
    d := with_data({"risk_score": null})
    not "risk_score_below_floor" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
}

# Two simultaneous failures must coexist in the deny set, not fail open.
test_multiple_denies_do_not_fail_open if {
    d := with_data({"apy_z_score": 99, "risk_score": 10})
    deny := vault_risk_rating.deny with data.params as default_params with data.wasm as d
    count(deny) >= 2
    not vault_risk_rating.allow with data.params as default_params with data.wasm as d
}
```

Run the tests:

```bash
opa test ./policy.rego ./policy_test.rego -v
```

Recommended coverage:

* **Happy path** — clean data allows.
* **Each deny rule** — one test per rule, asserting the expected reason is in `deny` and `allow` is false.
* **Param toggles** — a deny flag set to `false` does not deny.
* **Edge values** — `null`, empty, and out-of-range fields behave as intended.
* **No fail-open under multiple failures** — two deny conditions at once still denies.
* **Multiple policies** — test each policy with its own params and oracle fixture, then test the aggregate decision. There is no cross-policy `data.wasm` merge.

### Quick local checks with the CLI

The CLI uses the same Newton Regorus engine family as operator evaluation, including Newton's crypto and identity built-ins. Use it to evaluate a query and validate runtime syntax and built-in availability:

```bash
# Evaluate a query against policy + data + input files
newton-cli regorus eval \
  -d policy.rego \
  -d data.json \
  -i intent.json \
  --non-strict \
  "data.vault_risk_rating.allow"

# Validate syntax / inspect the policy
newton-cli regorus parse policy.rego
newton-cli regorus lex policy.rego
```

:::note
Pass `--non-strict` for OPA-style non-strict evaluation. This does not add unsupported OPA built-ins or make the engine fully OPA-compatible. `-d` (data/policy) can be repeated; `-i` is the input (intent) file. See the [CLI Reference](/developers/reference/command-line-tool#regorus).
:::

## Test your WASM oracle

Run the compiled oracle in isolation to confirm it produces the data shape your Rego expects.

```bash
# Local execution in a sandbox with the Newton HTTP host
newton-cli --chain-id 11155111 policy-data simulate \
  --wasm-file ./dist/policy.wasm \
  --input-json '{"network":"mainnet","vaultAddress":"0x..."}'
```

For an oracle that needs secrets, test through the Gateway:

| Method | Secrets source | Ownership |
|--------|----------------|-----------|
| `newt_simulatePolicyData` | Provided inline by the caller | None required |
| `newt_simulatePolicyDataWithClient` | Read from stored, uploaded secrets | PolicyClient owner |

```bash
curl -X POST https://gateway.testnet.newton.xyz/rpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $NEWTON_API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "newt_simulatePolicyData",
    "params": {
      "policy_address": "0xYourPolicy",
      "chain_id": 11155111,
      "wasm_args": "0x7b226e6574776f726b223a226d61696e6e6574227d"
    },
    "id": "7ca6621b-7aa4-4bb7-a896-1f2b58a18c78"
  }'
```

See [Deploying with CLI](/developers/guides/deploying-with-cli) for the current secrets workflow.

## Simulate the full policy set

After deploying policies and configuring `setPolicies`, call SDK `simulatePolicy` with `policyClient`, `chainId`, `intent`, and an ordered `wasmArgs` array. The gateway runs each policy's optional WASM and then Rego in-process. It reads params and entrypoints from the configured set; do not send inline Rego or `policy_data` lists.

```typescript
const simulation = await client.simulatePolicy({
  policyClient,
  chainId: 11155111,
  intent,
  wasmArgs: ['0x', '0x'], // Two pure-Rego policies
});
if (!simulation.success) throw new Error(simulation.error ?? 'Simulation failed');
console.log(simulation.policy_id, simulation.allowed);
console.table(simulation.policies);
```

For oracle-backed policies, replace each empty slot with that policy's hex-encoded oracle input. Use `newt_simulateTask` via raw RPC only when you already have oracle outputs; it skips WASM and resolves the current set, so it is not historical replay. See [RPC API](/developers/reference/rpc-api#newt_simulatetask).

## Policy-set validation checklist

Use two independent deterministic test policies, each with allow and deny fixtures. Keep the same client and record its ordered set before each run.

| Policy 0 | Policy 1 | Aggregate | Application state |
|---|---|---|---|
| Allow | Allow | Allow | Changes only after successful attested transaction |
| Allow | Deny | Deny | Unchanged |
| Deny | Allow | Deny | Unchanged |
| Deny | Deny | Deny | Unchanged |

* Check every input and result maps to its policy-set position; preserve repeated entries.
* Replace with a reordered set, then an identical set. Each successful replacement advances the revision and changes the ID. Old attestations must fail.
* Reject empty sets, more than 8 entries, unregistered policies, zero expiry, oversized params, and unauthorized updates on the pinned contract.
* Reject reused attestations and changes to sender, target, chain, value, calldata, or protected arguments. Verify expired responses and mismatched task snapshots fail.
* Read state before and after denial and failed validation. An API denial alone does not prove the contract rejects a deny attestation.

Record evidence separately:

| Level | Evidence |
|---|---|
| Local policy tests | Inputs, expected and actual Rego verdicts |
| Local contract tests | Configuration, intent binding, deny, stale response, and replay assertions |
| Gateway simulation | Endpoint, current set ID, per-policy results, aggregate verdict; no signatures |
| Live operator evaluation | Gateway task ID, frozen task, aggregate `allowed`, signed response |
| On-chain validation | Successful BLS validation or expected revert |
| Application execution | Transaction hash, receipt, and resulting application state |

Do not use wall-clock conditions for a deterministic tutorial. An allow decision applies to evaluation time; block-based expiration is not a promise that external conditions remain true until execution.

## Recommended workflow

1. Test each Rego policy locally with OPA and Newton Regorus.
2. Test optional WASM locally, then deploy each policy with its artifacts.
3. Configure the client's ordered set and upload required policy-scoped secrets.
4. Simulate with `newt_simulatePolicy` and cover every truth-table case.
5. Evaluate through live operators, then validate and execute an allowed transaction on-chain.
6. Verify denial, stale attestations, and replay cannot change application state.

## Next Steps

<Cards>
  <Card icon="bug" to="/developers/resources/testing-debugging" title="Testing & Debugging">
    Diagnose failures after deployment
  </Card>

  <Card icon="link" to="/developers/guides/composing-policy-sets" title="Composing Policy Sets">
    Simulate and test multi-oracle policies
  </Card>

  <Card icon="list-check" to="/developers/resources/deployment-checklist" title="Deployment Checklist">
    Pre-launch verification steps
  </Card>

  <Card icon="book" to="/developers/advanced/rego-syntax-guide" title="Rego Syntax Guide">
    Rego language reference
  </Card>
</Cards>
