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 (
4xxwith a body,200with"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?
Last updated on
8. Slots and translations
Fill the install slot with a walkthrough beside the form, ship its placeholder, and give every string the connector shows its ten translations.
10. Ship it
Take the Agora connector from green tests to a reviewed connector — the listing as creators will read it, the review checklist item by item, versioning, and what happens after Subscriby installs it.