9. Tests and the kit

Assemble the Agora connector's test suite — the three checks that need no application, the port tests against a faked forum, characterisation snapshots, the core paths through the fakes, and running the conformance kit.

The previous chapters each ended with a test. This chapter arranges them into a suite and adds what only the whole package can prove.

Three checks with no application

it('declares a manifest the SDK accepts', function (): void {
    $manifest = ManifestFile::load(__DIR__.'/../connector.json');

    expect($manifest->key)->toBe('agora');
});

it('creates only its own tables', function (): void {
    expect(MigrationRules::violations('agora', __DIR__.'/../database/migrations'))->toBe([]);
});

it('builds every button within the forum\'s limits', function (): void {
    $limits = ManifestFile::load(__DIR__.'/../connector.json')->messaging;

    MessageAction::url('Open the board', 'https://forum.example.com/b/12')->assertWithin($limits);
    MessageAction::command('Refresh my access', MemberCommand::ReissueGrants)->assertWithin($limits);
});

These run in the package alone, on every commit, and catch most kit failures before review.

The ports against a faked forum

Every port test from chapters 3 to 7 fakes Agora's HTTP API with Laravel's Http::fake(), one answer per call. Two habits from those chapters generalise:

  • Fake refusals as the platform sends them, in both shapes (4xx with a body, 200 with "ok": false), and assert the classified kind rather than the HTTP status.
  • Assert what was sent, not only what came back: the webhook events registered, the BBCode body, the member id in the board call.

Characterisation snapshots

Agora's wire format is the connector's contract with the forum. Pin it:

it('sends a sale notice as the same BBCode every time', function (): void {
    Http::fake(['forum.example.com/api/messages' => Http::response(['ok' => true, 'data' => ['id' => 'm_1']])]);

    $messenger->send($installation, $credentials, new Recipient($member), saleNoticeMessage());

    $sent = Http::recorded()->first()[0];

    expect($sent['body'])->toMatchSnapshot();
});

A snapshot that has to change is a behaviour change with its own commit and its own reason, never a side effect. Subscriby pins the Telegram connector's transcripts the same way, a hundred snapshots deep.

The core's paths through the fakes

Register Agora beside the SDK's fake connector in a Subscriby checkout and assert the same scenario on both, so a difference is either a manifest difference or a platform assumption left in the core:

it('grants a board when a member pays and revokes it when the subscription ends', function (): void {
    Http::fake([
        'forum.example.com/api/boards/12/members' => Http::response(['ok' => true]),
        'forum.example.com/api/boards/12/members/17' => Http::response(['ok' => true]),
        'forum.example.com/api/messages' => Http::response(['ok' => true, 'data' => ['id' => 'm_1']]),
    ]);

    $member = memberWithAgoraIdentity($project, externalId: '17');
    $subscription = settlePurchase($member, planGatingBoard(12));

    Http::assertSent(fn (Request $sent): bool => $sent->method() === 'POST' && str_ends_with($sent->url(), '/boards/12/members') && $sent['user_id'] === '17');

    cancelSubscription($subscription);

    Http::assertSent(fn (Request $sent): bool => $sent->method() === 'DELETE' && str_ends_with($sent->url(), '/boards/12/members/17'));
});

The helpers (memberWithAgoraIdentity, settlePurchase, planGatingBoard, cancelSubscription) drive the core's own actions through the application's factories; the Telegram connector's tests are the model to copy. The scenario a member with no forum identity produces is worth a test of its own: the grant waits as pending_identity and materialises when the member signs in through chapter 7's handshake.

Running the kit

Inside the Subscriby checkout, with the package under packages/subscriby-connector-agora/:

it('passes the conformance kit', function (): void {
    $report = (new ConformanceSuite(app(ConnectorRegistry::class)))
        ->run('agora', packagePath: dirname(__DIR__));

    expect($report->passed())->toBeTrue($report->summary());
});

For Agora the report has nineteen checks: the fourteen every connector gets, access.declares_kinds (we bind AccessController), recovery.vocabulary_and_readiness (we bind RecoverySupport), portal_login.button (we bind PortalLoginMethod), and the two on-disk rules. A green summary reads agora passes the conformance kit (19 checks).; a red one names each rule and its detail, and every one of them has a page under Conformance.

Outside a Subscriby checkout, the same test runs with new TestRegistry in place of app(ConnectorRegistry::class): register the manifest (ManifestFile::load(dirname(__DIR__).'/connector.json')) and your Connector class into it first, as Testing a connector shows. The registry refuses the connector before a rule runs if the manifest and the ports disagree, which is the refusal Subscriby's own boot would give.

Do not fake success

A faked forum that answers {"ok": true} to everything proves that your connector works on a forum that never refuses. Give every test at least one refusal in the shape Agora really sends.

How is this guide?

On this page

Subscriby is a product designed by you — for you.

No boardroom full of executives deciding what we ships next. Our roadmap always shaped by you with your feedback.

Share feedback or a request