conformance

package module
v0.0.0-...-5bedf67 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 29, 2025 License: MIT Imports: 27 Imported by: 0

README

Conformance tests for GoActivityPub storage backends

Usage

If you have implemented a backend for the GoActivityPub library, this here you have the test suite to verify that it will behave predictably for the other packages.

import (
    "testing"

    conformance "github.com/go-ap/storage-conformance-suite"
)

// TODO
// write your own initializing function that returns a ready to use instance
// of calls t.Fatal if errors are encountered.
var storageInit func(*testing.T) conformance.ActivityPubStorage

func Test_Conformance(t *testing.T) {
    suite := conformance.Suite(conformance.TestActivityPub, conformance.TestKey)
    suite.Run(t, storageInit(t))
}

Documentation

Index

Constants

View Source
const (
	TestActivityPub = 1
	TestKey         = 1 << iota
	TestPassword
	TestMetadata
	TestOAuth

	TestNone = 0

	TestsFull = TestActivityPub | TestKey | TestPassword | TestMetadata | TestOAuth
)

Variables

This section is empty.

Functions

func RandomActivity

func RandomActivity(withObject, parent vocab.Item) vocab.Item

func RandomActor

func RandomActor(parent vocab.Item) vocab.Item

func RandomCollection

func RandomCollection(cnt int) vocab.ItemCollection

func RandomImage

func RandomImage(mimeType vocab.MimeType, parent vocab.Item) vocab.Item
func RandomLink(attrTo vocab.Item) vocab.Item

func RandomObject

func RandomObject(parent vocab.Item) vocab.Item

func RandomTag

func RandomTag(parent vocab.Item) vocab.Item

func RunActivityPubTests

func RunActivityPubTests(t *testing.T, storage ActivityPubStorage)

func RunKeyTests

func RunKeyTests(t *testing.T, storage ActivityPubStorage)

func RunMetadataTests

func RunMetadataTests(t *testing.T, storage ActivityPubStorage)

func RunOAuthTests

func RunOAuthTests(t *testing.T, storage ActivityPubStorage)

func RunPasswordTests

func RunPasswordTests(t *testing.T, storage ActivityPubStorage)

Types

type ActivityPubStorage

type ActivityPubStorage interface {
	// Save saves te [vocab.Item] to storage.
	// To conform to what the GoActivityPub library expects out of it, there are a couple of hidden behaviours:
	// * When saving a [vocab.Object] compatible type the backend *MUST* create all the object's collections
	// that have IRIs set. These collections are in [vocab.OfObject].
	// Eg: For
	//
	//	vocab.Object{
	//		Likes: "https://example.com/objects/1/likes",
	//		Replies:"https://example.com/objects/1/replies",
	//		Shares: nil
	//	}
	//
	// We create the collections https://example.com/objects/1/likes, and https://example.com/objects/1/replies.
	// * When saving a [vocab.Actor] compatible type the backend *MUST* create all the actor's collections that
	// have IRIs set. These collections are in [vocab.OfActor].
	// Eg: For
	//
	//	vocab.Actor{
	//		Inbox:"https://example.com/~jdoe/inbox",
	//		Outbox: "https://example.com/~jdoe/outbox",
	//		Followers: "https://example.com/~jdoe/followers",
	//		Following: nil
	//	}
	//
	// We create the collections https://example.com/~jdoe/inbox, https://example.com/~jdoe/outbox and
	// "https://example.com/~jdoe/followers".
	Save(it vocab.Item) (vocab.Item, error)
	// Load loads the item found at the "iri" [vocab.IRI].
	// If iri points to a collection, the filters "f" get applied to the items of the collection.
	// An implicit assumption made by filters is that when the list contains checks for one level deep properties,
	// the storage backend loads those properties and replaces them into the original.
	// For example if filtering in an activities collection with a check that the Actor should have a preferred username
	// of "janeDoe", the actors of the activities need to be loaded and the check applied on them.
	// So when the object loaded is flattened to something like this:
	//
	//	vocab.Activity {
	//		Actor: "https://example.com/~jdoe" ...
	//	}
	//
	// the actor gets dereferenced to:
	//
	//	vocab.Activity{
	//		Actor: vocab.Actor{
	//			ID: "https://example.com/~jdoe",
	//			preferredUsername: "janeDoe" ...
	//		}
	//	}
	//
	// which then can be filtered with the "preferredUsername" check.
	// The filters can also contain pagination checks, and when those get applied (see the github.com/go-ap/filtering package)
	// the result can be different from the actual persisted value.
	// The [filters.Checks.Paginate] method should handle most cases, so it should be enough to call it just before
	// returning, similarly to how the local "memStorage" type does.
	Load(iri vocab.IRI, ff ...filters.Check) (vocab.Item, error)
	Delete(it vocab.Item) error

	// Create
	// NOTE(marius): should we remove this in favour of custom logic for Save()?
	Create(col vocab.CollectionInterface) (vocab.CollectionInterface, error)

	// AddTo adds items to the collection.
	// Similarly to Save(), this method needs to implement some hidden behaviour in order to conform to the expectations
	// of the rest of GoActivityPub library when executing logic for the blocking and ignoring ActivityPub operations.
	// In summary, when adding items to the "blocked" or "ignored" collections of a [vocab.Actor]
	// they need to be created if missing.
	// These two collections are called "hidden" because they do not appear as properties on the Actor,
	// so the only way to build their IDs is to append the paths "/blocked" and "/ignored" the Actor's ID.
	AddTo(colIRI vocab.IRI, items ...vocab.Item) error
	RemoveFrom(colIRI vocab.IRI, items ...vocab.Item) error
}

type ClientLister

type ClientLister interface {
	ListClients() ([]osin.Client, error)
	GetClient(id string) (osin.Client, error)
}

type ClientSaver

type ClientSaver interface {
	UpdateClient(c osin.Client) error
	CreateClient(c osin.Client) error
	RemoveClient(id string) error
}

type KeyStorage

type KeyStorage interface {
	LoadKey(iri vocab.IRI) (crypto.PrivateKey, error)
	SaveKey(iri vocab.IRI, key crypto.PrivateKey) (*vocab.PublicKey, error)
}

type MetadataStorage

type MetadataStorage interface {
	LoadMetadata(iri vocab.IRI, m any) error
	SaveMetadata(iri vocab.IRI, m any) error
}

type NilCloser

type NilCloser interface {
	Close()
}

type OSINStorage

type OSINStorage interface {
	Clone() osin.Storage
	Close()
	GetClient(id string) (osin.Client, error)
	SaveAuthorize(*osin.AuthorizeData) error
	LoadAuthorize(code string) (*osin.AuthorizeData, error)
	RemoveAuthorize(code string) error
	SaveAccess(*osin.AccessData) error
	LoadAccess(token string) (*osin.AccessData, error)
	RemoveAccess(token string) error
	LoadRefresh(token string) (*osin.AccessData, error)
	RemoveRefresh(token string) error
}

OSINStorage is a verbatim copy of the osin.Storage interface We use this method instead of aliasing it, so it's more obvious what needs to be implemented.

type Opener

type Opener interface {
	Open() error
}

type PassAndKeyMetadata

type PassAndKeyMetadata struct {
	Pw  string            `json:"pw"`
	Key crypto.PrivateKey `json:"key"`
}

func (PassAndKeyMetadata) MarshalJSON

func (p PassAndKeyMetadata) MarshalJSON() ([]byte, error)

func (*PassAndKeyMetadata) UnmarshalJSON

func (p *PassAndKeyMetadata) UnmarshalJSON(raw []byte) error

type PasswordStorage

type PasswordStorage interface {
	PasswordSet(it vocab.IRI, pw []byte) error
	PasswordCheck(it vocab.IRI, pw []byte) error
}

type TestType

type TestType uint16

func Suite

func Suite(tt ...TestType) TestType

func (TestType) Run

func (tt TestType) Run(t *testing.T, storage ActivityPubStorage)

Directories

Path Synopsis
names
Package names is copy/pasted from docker's code source The original can be found in package: "github.com/docker/docker/pkg/namesgenerator"
Package names is copy/pasted from docker's code source The original can be found in package: "github.com/docker/docker/pkg/namesgenerator"

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL