# Testing your integration

You need to see what happens when content is rejected, without uploading real harmful content. CalmLens recognises **test inputs**: harmless images and text codes that it classifies as a chosen category with a score of 95%. Your project's policy then applies its normal action, so you can check your rejection, review queue and webhook handling end to end.

Use offline mocks for application logic and real test uploads for policies, events and delivery. Real test inputs count towards uploads and usage; mocks do not.

## Unit tests without the network

For tests that shouldn't call CalmLens at all, use your SDK's mock client to simulate approvals and rejections:

**python**

```python
from calmlens.testing import MockCalmLensClient

client = MockCalmLensClient(mock_options={"analyzed_status": "rejected"})
result = client.assets.moderate({'name': 'text', 'content': {'plainText': 'anything', 'type': 'text/plain'}})
assert result["status"] == "rejected"
```

**go**

```go
package main

import (
    "context"
    "fmt"
    "log"
    calmlens "github.com/Censkh/CalmLens/packages/go-sdk"
    "github.com/Censkh/CalmLens/packages/go-sdk/calmlenstest"
)

func main() {
    client, err := calmlenstest.NewClient(calmlenstest.Options{AnalyzedStatus: "rejected"})
    if err != nil { log.Fatal(err) }
    result, err := client.Assets.Moderate(context.Background(), calmlens.SubmitAssetOptions{Name: "text", Content: &calmlens.Content{Type: "text/plain", PlainText: calmlens.Ptr("anything")}})
    if err != nil { log.Fatal(err) }
    fmt.Println(result.Status)
}

```

**JavaScript**

```javascript
import { MockCalmLensClient, createMockApiKey } from "@calmlens/sdk/testing";

const client = new MockCalmLensClient({
  apiKey: createMockApiKey(),
  projectId: "123e4567-e89b-12d3-a456-426614174000",
  mockOptions: { analyzedStatus: "rejected" },
});

const result = await client.assets.moderate({ name: "text", content: { plainText: "anything", type: "text/plain" } });
// result.status === "rejected"
```

## Test real moderation

**Test images:** PNGs with four coloured corner squares, each sampled at the centre of a corner third. Download them from the web documentation page.

| Signal | Corner RGB (top-left, top-right, bottom-left, bottom-right) |
| --- | --- |
| Sexual Content (`sexual`) | 255,20,147 · 255,105,180 · 199,21,133 · 255,69,0 |
| Violence (`violence`) | 139,0,0 · 220,20,60 · 178,34,34 · 255,0,0 |
| Graphic violence (`violence.graphic`) | 128,0,0 · 165,42,42 · 139,69,19 · 160,82,45 |
| Self-Harm (`self-harm`) | 75,0,130 · 138,43,226 · 72,61,139 · 147,112,219 |

**Test text codes** (replace `XXXXXXXX` with the last 8 characters of your project ID):

- `<!calmlens-test XXXXXXXX harassment>`
- `<!calmlens-test XXXXXXXX hate>`
- `<!calmlens-test XXXXXXXX criminal-activity>`

### How test inputs behave

- **Images** must be PNGs with the coloured corners intact. Resizing, recompressing to JPEG or cropping can stop them matching. The image test is checked before any moderation provider is called.
- **Text codes** can appear anywhere in text content, including captions, comments and documents. They only work in the project whose ID ends with the code's 8 characters; in any other project the text is moderated normally.
- The category scores 95% and every other category scores 1%. With the built-in policies that usually means `rejected` for images, and `rejected` or `approved` with `report.flagged: true` for text, depending on the category's action.

Signed-in users can also open these from **Assets → Try test inputs** in the portal, where the text codes already include your project ID.

## Audio and video test codes

Audio (beta) · Video (beta)

Open **Assets → Try test inputs** in a project to copy its media codes. Read a code clearly in a recording, or show it as large, high-contrast text in a video for at least two seconds. Leave the whole phrase visible, including “Calm Lens test” and “end test”. Native media processing checks for the complete project phrase in each chunk; detection depends on readable text, clear speech and model recognition. The legacy path samples video once per second and uses OCR and speech transcription. Codes that cross a chunk boundary can be missed despite the one-second overlap.

For a project whose ID ends in `89abcdef`, a violence trigger is:

```text
Calm Lens test eight nine alpha bravo charlie delta echo foxtrot violence end test
```

For a shorter visible overlay, the same code can use `Calm Lens test 89abcdef violence end test`. When speaking, read the eight project characters individually with a short pause between them.

The eight words encode the last eight hexadecimal characters of the project ID. Digits use `zero` through `nine`; letters use `alpha`, `bravo`, `charlie`, `delta`, `echo`, and `foxtrot`. Supported categories are `sexual`, `violence`, `self harm`, `harassment`, `hate`, `criminal activity`, and `violence graphic`. A code for another project receives ordinary moderation.

JavaScript integrations can generate the exact phrase:

```ts
import { generateMediaTestCode } from "@calmlens/sdk/testing";
const phrase = generateMediaTestCode("violence", projectId);
```

You can also turn a downloaded test PNG into a video with FFmpeg:

```sh
ffmpeg -loop 1 -i calmlens-test-violence.png -t 3 -c:v libx264 -pix_fmt yuv420p test-violence.mp4
```

Keep all four coloured corners in the frame. Coloured-corner image markers are supported by the legacy frame pipeline; they are not recognized as test markers by native media processing. Use a complete media phrase for native tests. Legacy markers tolerate moderate compression; arbitrary cropping, overlays or colour changes may prevent detection. Test codes and markers trigger the normal policy decision, including rejection, alerts or purging. They do not simulate a decoder or provider outage. Test invalid media separately by uploading a corrupt file with an audio/video MIME type and checking the eventual `error` status.
