Skip to content

Run the conformance harness

The compiler checks that your provider has the right methods. It cannot check that those methods behave the way a consumer relies on: that a read-only config yields nothing to post through, that Close is safe twice, that the capabilities you declare are the ones you have.

RunProviderConformance checks that, and it runs without a network.

Add it to your provider's tests

package myplatform_test

import (
    "testing"

    chatplatform "gitlab.com/phpboyscout/go/chat-platform"
    chatplatformtest "gitlab.com/phpboyscout/go/chat-platform/test"
    myplatform "gitlab.com/phpboyscout/go/chat-platform-myplatform"
)

func TestConformance(t *testing.T) {
    t.Parallel()

    chatplatformtest.RunProviderConformance(t, chatplatformtest.ConformanceConfig{
        NewClient: func(cfg chatplatform.ClientConfig) (chatplatform.Client, error) {
            return myplatform.New(cfg)
        },
        Capabilities: chatplatformtest.Capabilities{
            Moderator:       true,
            MemberInspector: true,
            Interactive:     true,
            Commands:        true,
        },
        Space:          "1531227937678954747",
        AllowedChannel: "1531227938622800055",
    })
}

Space and AllowedChannel must be syntactically valid identifiers for your platform. They are never dialled; the harness only needs values your constructor will accept.

Declare capabilities honestly

Capabilities exists so the harness can tell "correctly does not offer this" apart from "should and does not". It is checked in both directions:

  • Claim one you lack, and the harness looks for something impossible.
  • Omit one you have, and it tells you, because a capability nobody declares is a capability nobody discovers.

Declare what you can never serve

A transport that carries audio only (a voice node with no gateway) cannot serve NeedMessages, and the contract says it refuses that at construction rather than carrying it onto a scope whose Messages() never yields. Tell the harness with Refuses:

chatplatformtest.ConformanceConfig{
    NewClient: func(cfg chatplatform.ClientConfig) (chatplatform.Client, error) {
        // Discord's node takes no ClientConfig at all; only the needs cross.
        return voicenode.New(voicenode.Config{Control: fakeLink, Self: self, Needs: cfg.Needs})
    },
    Refuses:      []chatplatform.Need{chatplatform.NeedMessages},
    Capabilities: chatplatformtest.Capabilities{VoiceReceiver: true, VoiceSender: true},
    // ...
}

For each need named, the harness asks for a client with that need alone and requires a nil Client and an error matching ErrUnsupported that names the need. The rest of the run declares every need except those, so the transport is tested for what it serves. Like Capabilities, it is trusted: a transport that should refuse and names nothing here is not caught.

Why it runs offline

Because it has to run in CI, on a fork, and on a laptop with no credentials.

That constraint is load-bearing rather than a convenience, and it is worth stating plainly: if your provider cannot be constructed without a real token, it cannot be conformance-tested. The Discord provider failed its very first run for exactly this reason, and the fix was to move client construction out of New and into Connect, which is a better contract anyway. See Author a provider.

What belongs elsewhere

Behaviour needing a live platform (that a thread is really created, that the allowlist really filters an inbound feed, that a dropped connection really resumes) belongs in your own integration tests, gated behind an environment variable or a build tag.

The chat-platform-discord module keeps its forced-disconnect regression test behind an integration build tag for this reason: it cuts a real TCP connection and needs live credentials, so it is run locally and before a release rather than on every merge.

Reading a failure

Failures are reported as method: message. Treat one as a finding about your provider before you treat it as one about the harness.

On the Discord provider's first run the harness reported that the provider could not be constructed at all, and that an implemented capability was not declared. Both were real.

What the harness checks, and what it leaves to you

Every assertion it makes, the exact message each produces, and the list of real contract behaviour it cannot check offline (the allowlist, ErrNotConnected, Connect itself) are in the conformance harness reference.

Passing means the contract a consumer depends on before a single message arrives is honoured. It does not mean the provider works.