# Testing and distribution

Test extension logic outside Mango where possible, then verify the real bridge, consent, sandbox, and presentation in a development build.

## Keep logic testable

Put service clients, parsers, filter decisions, and state transitions in ordinary JavaScript or TypeScript modules that do not import the Grove global. Pass the small inputs they need and test them with Node's built-in test runner or the framework already used by the package.

For bridge-facing code, create a narrow adapter or fake:

```js
export function startWeatherCommands(grove, weatherClient) {
  grove.commands.register([{ name: 'weather', usage: '<city>' }])
  grove.on('command', async invocation => {
    const result = await weatherClient.current(invocation.argString)
    grove.card({ id: 'weather', title: invocation.argString, body: result })
  })
}
```

A test can supply an object that records `register`, captures the event listener, and inspects the emitted card without starting WebKit or opening IRC.

The tagged public examples demonstrate this approach:

- [hello-vue v0.1.0](https://github.com/Mediaware-EU/hello-vue/tree/v0.1.0) for a visible Vue and TypeScript Sprig;
- [giphy-composer v0.1.10](https://github.com/Mediaware-EU/giphy-composer/tree/v0.1.10) for an application-scoped composer utility, on-demand slash commands, and settings;
- [command-lab-root v0.1.0](https://github.com/Mediaware-EU/command-lab-root/tree/v0.1.0) for focused command, card, send, and event tests;
- [weatherbot v0.2.1](https://github.com/Mediaware-EU/weatherbot/tree/v0.2.1) for commands, cards, badges, network egress, storage, and channel events;
- [relay-formatter v0.1.2](https://github.com/Mediaware-EU/relay-formatter/tree/v0.1.2) for inbound filtering and member actions;
- [fish-root v0.1.2](https://github.com/Mediaware-EU/fish-root/tree/v0.1.2) for inbound/outbound filters and Keychain-backed legacy interoperability;
- [tus-uploader v0.2.2](https://github.com/Mediaware-EU/tus-uploader/tree/v0.2.2) for native upload-provider contribution and resumable streaming;
- [immich-composer v0.1.0](https://github.com/Mediaware-EU/immich-composer/tree/v0.1.0) for a user-selected self-hosted service, brokered native OAuth with PKCE, bounded non-CORS API requests, Keychain session storage, binary thumbnails, and composer completion.

## Verify in Mango

Use **Load Unpacked (Dev)…** on macOS for the authoring loop. Exercise at least:

- first-run consent and a denied permission;
- the intended channel, query, server, or application scope;
- every declared external origin and a request to one undeclared origin;
- extension stop and restart;
- invalid or missing stored configuration;
- network loss, reconnect, and application background/foreground transitions;
- malformed IRC content and backend responses;
- the Console path for JavaScript and host-side errors.

Use **Install from Folder…** to test the copied-package behavior that users receive. A live development folder can change without reinstalling; an installed folder is a snapshot.

Load Unpacked is macOS-only. iPhone and iPad testing uses a copied folder or `.grove` archive.

## Build a `.grove` package

Build the static production output first. The archive should contain `manifest.json`, the declared entry and settings files, and every referenced asset. It may contain those files at the archive root or under one wrapping directory.

Name the zip with a `.grove` extension. Do not package `node_modules`, source-only test fixtures, local credentials, `.env` files, signing keys, or a development-server URL.

Before publishing, install the final archive in Mango rather than testing only its source folder. This catches missing build output, incorrect relative paths, casing differences, and assets accidentally left outside the package.

## Catalogue distribution

The curated Mango extension catalogue is generated from repository examples and website metadata. A catalogue entry points at an HTTPS `.grove` package and includes integrity metadata used by Mango's update flow.

Mango's own catalogue pins a full source commit for every current release and generates the `.grove` archive during the website build. Archives are build artifacts and are not committed to the repository. Publishing is therefore a two-commit operation: commit the extension source and version first, then point the catalogue at that immutable source commit and regenerate its JSON metadata. Increment the version whenever the resulting package bytes change.

Installation and starting are separate actions. Mango downloads and validates the package but does not start it until the user opens Grove Manager and accepts the requested capabilities. An update is also explicit, verifies package identity and integrity, stops the previous runtime, and clears the previous permission grant.

Grove API 1 does not yet provide third-party publisher signatures or a public trust hierarchy. Catalogue review and HTTPS package hosting do not replace careful source review, minimal capabilities, exact network origins, or safe handling of untrusted data.

## Versioning

Use semantic versions for extension releases so the catalogue can order updates. Keep the `id` unchanged across versions. Increment the package version whenever distributed bytes change.

Keep `grove: 1` while using the API 1 contract. Do not raise the integer for an ordinary extension release. A future Mango release will define a new integer only for a breaking Grove API.

When adopting a newly documented additive API 1 feature, state the minimum Mango release in the extension README until the manifest gains formal minimum-client compatibility fields.
