anetostest

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Overview

Package anetostest tests Anetos applications: it boots the app with test settings and a clean database, sends requests through the router like a browser (cookies, CSRF tokens, the Referer), and asserts on responses, sessions, database rows, files, and the jobs, events, email and pub/sub messages the app sent (AssertDispatched, AssertEmitted, AssertMailSent, AssertPublished; faked with FakeQueue, FakeEvents, FakePubSub), and the model requests it made, with scripted answers (FakeAI, App.AssertPrompted). App.Freeze and App.Travel control the app's clock.

func TestCreatePost(t *testing.T) {
	app := anetostest.New(t, setup) // setup: the app's own wiring
	author := anetostest.Create(app, factories.Authors)

	app.Get("/posts/new")
	app.PostForm("/posts", url.Values{"title": {"Hello"}, "author_id": {fmt.Sprint(author.ID)}}).
		AssertRedirect("/posts").
		AssertSessionHas("status", "Post created.")

	anetostest.AssertDatabaseHas[models.Post](app, models.PostCols.Title.Eq("Hello"))
}

Make one App per test or subtest: it reports to the t it was made with. With SQLite and no DB_DATABASE, each App gets its own in-memory database; with PostgreSQL or MySQL (DB_* in the environment or .env.testing) or a SQLite file, migrations run and everything the test does happens in a transaction that is rolled back at the end.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertDatabaseCount

func AssertDatabaseCount[T any](a *App, want int64, conds ...db.Expr)

AssertDatabaseCount checks the number of T rows matching conds.

func AssertDatabaseHas

func AssertDatabaseHas[T any](a *App, conds ...db.Expr)

AssertDatabaseHas checks that a T row matches conds, as db.Query[T] sees it (soft-deleted rows don't count; see AssertSoftDeleted).

anetostest.AssertDatabaseHas[models.Post](app, models.PostCols.Title.Eq("Hello"))

func AssertDatabaseMissing

func AssertDatabaseMissing[T any](a *App, conds ...db.Expr)

AssertDatabaseMissing checks that no T row matches conds.

func AssertDispatched added in v0.2.0

func AssertDispatched[J queue.Job](a *App, match func(J) bool)

AssertDispatched checks that a job of type J for which match returns true (any, for a nil match) was dispatched.

anetostest.AssertDispatched(app, func(j ChargeOrder) bool { return j.OrderID == id })

func AssertEmitted added in v0.2.0

func AssertEmitted[E any](a *App, match func(E) bool)

AssertEmitted checks that an event of type E for which match returns true (any, for a nil match) was emitted.

anetostest.AssertEmitted(app, func(e OrderPlaced) bool { return e.Cents == 1500 })

func AssertMailNotSent added in v0.2.0

func AssertMailNotSent[M mailer.Mailable](a *App, match func(M) bool)

AssertMailNotSent checks that no mailable of type M for which match returns true (none at all, for a nil match) was sent or queued.

func AssertMailQueued added in v0.2.0

func AssertMailQueued[M mailer.Mailable](a *App, match func(M) bool)

AssertMailQueued checks that a mailable of type M for which match returns true (any, for a nil match) was queued with mailer.Queue.

func AssertMailSent added in v0.2.0

func AssertMailSent[M mailer.Mailable](a *App, match func(M) bool)

AssertMailSent checks that a mailable of type M for which match returns true (any, for a nil match) was sent with mailer.Send.

anetostest.AssertMailSent(app, func(m mails.Welcome) bool { return m.User.ID == u.ID })

func AssertNotDispatched added in v0.2.0

func AssertNotDispatched[J queue.Job](a *App, match func(J) bool)

AssertNotDispatched checks that no job of type J for which match returns true (none at all, for a nil match) was dispatched.

func AssertNotEmitted added in v0.2.0

func AssertNotEmitted[E any](a *App, match func(E) bool)

AssertNotEmitted checks that no event of type E for which match returns true (none at all, for a nil match) was emitted.

func AssertNotPublished added in v0.2.0

func AssertNotPublished[T any](a *App, topic string, match func(T) bool)

AssertNotPublished checks that no message for which match returns true (none at all, for a nil match) was published to topic, decoded as T (messages that don't decode as T don't match).

func AssertPublished added in v0.2.0

func AssertPublished[T any](a *App, topic string, match func(T) bool)

AssertPublished checks that a message for which match returns true (any, for a nil match) was published to topic, decoded as T (messages that don't decode as T don't match).

anetostest.AssertPublished(app, "orders.created", func(m OrderCreated) bool { return m.ID == id })

func AssertSoftDeleted

func AssertSoftDeleted[T any](a *App, conds ...db.Expr)

AssertSoftDeleted checks that a soft-deleted T row matches conds. T must embed db.SoftDeletes.

func Create

func Create[T any](a *App, f *factory.Factory[T]) T

Create inserts a row made by f, in the test's database and transaction.

post := anetostest.Create(app, factories.Post.With(func(p *models.Post) { p.Draft = true }))

func CreateMany

func CreateMany[T any](a *App, f *factory.Factory[T], n int) []T

CreateMany inserts n rows made by f.

func Events added in v0.2.0

func Events[E any](a *App) []E

Events returns the events of type E (or, for an interface type, that implement it) emitted so far, oldest first.

func Jobs added in v0.2.0

func Jobs[J queue.Job](a *App) []J

Jobs returns the jobs of type J dispatched so far, oldest first, decoded from what was dispatched (as a worker would).

jobs := anetostest.Jobs[ChargeOrder](app)

func Mailables added in v0.2.0

func Mailables[M mailer.Mailable](a *App) []M

Mailables returns the mailables of type M sent or queued so far, oldest first.

func Messages added in v0.2.0

func Messages[T any](a *App, topic string) []T

Messages returns the messages published to topic so far, oldest first, decoded from JSON as T.

Types

type App

type App struct {
	*anetos.App
	// contains filtered or unexported fields
}

App is an application under test. It embeds the *anetos.App, and sends requests to the app's router with the methods of this type. Requests run one at a time; an App isn't safe for concurrent use (the database transaction isn't), but tests with their own App may run in parallel.

func ActingAs added in v0.3.0

func ActingAs[U auth.Authenticatable](a *App, u U) *App

ActingAs signs u in for the requests that follow, as a password sign-in without remember-me would, so a test needn't post the login form. It replaces whoever was signed in, and drops a remember-me cookie. U is the user type given to auth.ForApp in setup (*models.User):

app := anetostest.New(t, setup)
ada := anetostest.Create(app, factories.Users)
anetostest.ActingAs(app, &ada).Get("/dashboard").AssertOK()

It needs sessions (session.ForApp) and auth.ForApp in setup; a disabled user fails the test.

func New

func New(t testing.TB, setup func(app *anetos.App) (*web.Server, error), opts ...Option) *App

New builds the app with setup (the function main uses to connect the database and add the server and routes; nil for none), boots it and prepares its database. It closes the app when the test ends.

Settings, from highest priority: Env options; APP_ENV=testing, a random APP_KEY, and a CACHE_PREFIX, SESSION_PREFIX, QUEUE_PREFIX and PUBSUB_PREFIX of the App's own (so tests sharing a store don't see each other's items; New removes the App's items, sessions, Redis jobs and streams when the test ends), MAIL_DRIVER=memory (emails are kept, not sent: check them with the mailer's MemoryTransport), STORAGE_DRIVER=memory (files are kept in memory, for every disk that doesn't set its own driver), AI_PROVIDER=fake (no model is called: FakeAI scripts the answers) and an empty AI_EMBEDDING_PROVIDER (so embeddings are AI_PROVIDER's: the fake's); the process environment; the .env.testing file next to go.mod, if there is one (say, DB_DATABASE=blog_test); then HTTP_ACCESS_LOG=false, APP_URL=http://example.test (the test client's site, for absolute links, in emails say) and MAIL_FROM_ADDRESS=test@example.com. The settings in .env are not used (New only looks at its DB_CONNECTION, to stop a test that would use SQLite by mistake: when .env uses PostgreSQL, say, the test settings must name a DB_CONNECTION too, if only DB_CONNECTION=sqlite). With SQLite and neither DB_DATABASE nor DB_URL set (or set to ""), the database is in memory, not database/app.db.

After setup, New records what the app's queue, event bus, mailer and pub/sub dispatch, emit, mail and publish, for AssertDispatched and the other assertions; FakeQueue, FakeEvents and FakePubSub make them record only. The app's clock is the test's (App.Freeze, App.Travel).

func (*App) AI added in v0.3.0

func (a *App) AI() *ai.Fake

AI returns the fake provider of the app's AI client: its requests so far (Requests), and more replies (Add). The test fails if the app has no AI client, or AI_PROVIDER isn't fake and FakeAI wasn't used.

func (*App) AssertNoMail added in v0.2.0

func (a *App) AssertNoMail() *App

AssertNoMail checks that no email was sent or queued.

func (*App) AssertNoRepeatedQueries added in v0.2.0

func (a *App) AssertNoRepeatedQueries() *App

AssertNoRepeatedQueries checks that no request (or job, listener…) of the test ran a query repeatedly: no N+1.

app.GetJSON("/posts?with=author").AssertOK()
app.AssertNoRepeatedQueries()

func (*App) AssertNotPrompted added in v0.3.0

func (a *App) AssertNotPrompted() *App

AssertNotPrompted checks that the app made no request to the model.

func (*App) AssertNothingDispatched added in v0.2.0

func (a *App) AssertNothingDispatched() *App

AssertNothingDispatched checks that no job was dispatched.

func (*App) AssertNothingEmitted added in v0.2.0

func (a *App) AssertNothingEmitted() *App

AssertNothingEmitted checks that no event was emitted.

func (*App) AssertPrompted added in v0.3.0

func (a *App) AssertPrompted(match func(ai.Request) bool) *App

AssertPrompted checks that a request to the model matched match (nil matches any request).

app.AssertPrompted(func(r ai.Request) bool { return strings.Contains(r.Prompt(), "1042") })

func (*App) Context

func (a *App) Context() context.Context

Context returns the context the test's requests run with: it carries what the app provides (the database) and the test's transaction. Use it to call the app's code and the db package directly.

func (*App) Delete

func (a *App) Delete(path string) *Response

Delete sends a DELETE request without a body.

func (*App) DeleteForm

func (a *App) DeleteForm(path string, form url.Values) *Response

DeleteForm sends a DELETE request with an URL-encoded form.

func (*App) DeleteJSON

func (a *App) DeleteJSON(path string) *Response

DeleteJSON sends a DELETE request accepting JSON, without a body.

func (*App) Disk added in v0.2.0

func (a *App) Disk(name ...string) *Disk

Disk returns the app's default disk, or the disk name (one of STORAGE_DISKS), for assertions:

app.Disk("avatars").AssertExists("users/1.png")

func (*App) Dispatched added in v0.2.0

func (a *App) Dispatched() []queue.Dispatched

Dispatched returns the jobs dispatched so far, oldest first: those of every type, function jobs ("mail:send") included.

func (*App) Do

func (a *App) Do(req *http.Request) *Response

Do sends req, adding what the other methods add when it lacks them: the jar's cookies (by name), the headers set with App.WithHeader, the Referer, and (for methods other than GET, HEAD, OPTIONS and TRACE) the X-CSRF-Token header, which takes precedence over a "_token" form field. It runs with the test's context (App.Context). Build req with httptest.NewRequest and a path:

req := httptest.NewRequest(http.MethodDelete, "/notes/1", nil)
req.Header.Set("HX-Request", "true")
app.Do(req).AssertOK()

func (*App) Emitted added in v0.2.0

func (a *App) Emitted() []any

Emitted returns the events emitted so far, oldest first.

func (*App) Freeze added in v0.2.0

func (a *App) Freeze(t time.Time) time.Time

Freeze stops the app's clock at t (at the current time, for the zero time), in UTC and to the microsecond (as databases store times), and returns it. Everything that reads the time from the app's clock (anetos.Now, app.Now) sees it: model timestamps, the expiry of sessions, cookies, tokens, signed URLs and cached items, dates in validation rules, emails' Date. Time kept by database and Redis servers, and timeouts, aren't affected. The clock stays frozen until App.Unfreeze.

now := app.Freeze(time.Time{})
app.PostJSON("/posts", post)
anetostest.AssertDatabaseHas[models.Post](app, models.PostCols.CreatedAt.Eq(now))

func (*App) Get

func (a *App) Get(path string) *Response

Get sends a GET request, as a browser following a link.

func (*App) GetJSON

func (a *App) GetJSON(path string) *Response

GetJSON sends a GET request accepting JSON, as an API client.

func (*App) Head

func (a *App) Head(path string) *Response

Head sends a HEAD request.

func (*App) Mail added in v0.2.0

func (a *App) Mail() []mailer.Record

Mail returns the emails sent with mailer.Send and queued with mailer.Queue so far, oldest first, with their mailables. (The emails the transport got, queued ones the queue ran included, are the mailer's MemoryTransport's.)

func (*App) PatchForm

func (a *App) PatchForm(path string, form url.Values) *Response

PatchForm sends a PATCH request with an URL-encoded form.

func (*App) PatchJSON

func (a *App) PatchJSON(path string, body any) *Response

PatchJSON sends a PATCH request with body encoded as JSON, accepting JSON.

func (*App) PostForm

func (a *App) PostForm(path string, form url.Values) *Response

PostForm sends a POST request with an URL-encoded form, as a browser submitting a form. The session's CSRF token is sent in the X-CSRF-Token header, unless form has a "_token" field or the header is set.

func (*App) PostJSON

func (a *App) PostJSON(path string, body any) *Response

PostJSON sends a POST request with body encoded as JSON, accepting JSON.

func (*App) PostMultipart added in v0.2.0

func (a *App) PostMultipart(path string, form url.Values, files ...Upload) *Response

PostMultipart sends a POST request with a multipart form, as a browser uploading files: the fields of form, then the files. The CSRF token is sent as with App.PostForm.

app.PostMultipart("/documents", nil, anetostest.Upload{Field: "file", Filename: "a.pdf", Content: pdf})

func (*App) Published added in v0.2.0

func (a *App) Published() []pubsub.Published

Published returns the messages published so far, oldest first.

func (*App) PutForm

func (a *App) PutForm(path string, form url.Values) *Response

PutForm sends a PUT request with an URL-encoded form. Browsers send forms with POST only; to test a form using a "_method" field (see web.MethodOverride), use App.PostForm with the field.

func (*App) PutJSON

func (a *App) PutJSON(path string, body any) *Response

PutJSON sends a PUT request with body encoded as JSON, accepting JSON.

func (*App) RepeatedQueries added in v0.2.0

func (a *App) RepeatedQueries() []db.RepeatedQuery

RepeatedQueries returns the queries a request (or job, listener…) of the test ran repeatedly (DB_REPEATED_QUERIES times or more, default 5 in tests), oldest first: each is an N+1 to fix. They are also logged as warnings.

func (*App) Router

func (a *App) Router() *web.Router

Router returns the app's router, or nil if setup returned no server.

func (*App) Session

func (a *App) Session() *session.Session

Session returns the session the next request will carry, to inspect. Changes to it are not saved; use App.WithSession.

func (*App) SocialSignIn added in v0.2.0

func (a *App) SocialSignIn(redirect string, acct SocialAccount) *Response

SocialSignIn signs in with acct through social login, as a browser would: it follows redirect (the app's route that sends users to the provider, "/auth/google/redirect", with ?remember=1 if you like), plays the provider's sign-in page for acct, and returns the app's answer to the callback: a redirect to the intended page or AUTH_HOME_URL, or back to the login page with a "social" error. It needs FakeSocial.

func (*App) Travel added in v0.2.0

func (a *App) Travel(d time.Duration)

Travel moves the app's clock by d (back, if d is negative): a frozen clock stays frozen at its new time; a running one keeps running, d ahead.

app.Travel(31 * time.Minute) // past the reset link's lifetime

func (*App) Unfreeze added in v0.2.0

func (a *App) Unfreeze()

Unfreeze returns the app's clock to the system's time.

func (*App) WithHeader

func (a *App) WithHeader(name, value string) *App

WithHeader sets a header on every later request.

func (*App) WithSession

func (a *App) WithSession(fn func(s *session.Session)) *App

WithSession changes the session later requests carry, as if earlier requests had stored the values: a signed-in user, a cart. It needs the session middleware's manager (session.ForApp).

type Disk added in v0.2.0

type Disk struct {
	// contains filtered or unexported fields
}

Disk checks the files of one of the app's disks. In tests, disks keep their files in memory (STORAGE_DRIVER=memory), so each test starts with empty disks, unless a named disk sets its own driver (STORAGE_<NAME>_DRIVER).

func (*Disk) AssertContent added in v0.2.0

func (d *Disk) AssertContent(path, want string) *Disk

AssertContent checks that the file exists with the content want.

func (*Disk) AssertExists added in v0.2.0

func (d *Disk) AssertExists(paths ...string) *Disk

AssertExists checks that the files exist.

func (*Disk) AssertMissing added in v0.2.0

func (d *Disk) AssertMissing(paths ...string) *Disk

AssertMissing checks that the files don't exist.

func (*Disk) Files added in v0.2.0

func (d *Disk) Files(prefix string) []string

Files returns the paths of the disk's files under prefix ("" for all), in order.

func (*Disk) Storage added in v0.2.0

func (d *Disk) Storage() *storage.Disk

Storage returns the disk's storage.Disk, to read or add files.

type Option

type Option func(*options)

Option configures New.

func Env

func Env(values map[string]string) Option

Env sets configuration values, over the test defaults and the environment.

func FakeAI added in v0.3.0

func FakeAI(replies ...ai.FakeReply) Option

FakeAI scripts the model's answers: each request to the app's AI client gets the next reply, in order (ai.FakeText, ai.FakeObject, ai.FakeToolCall, ai.FakeError), and no model is called: New sets AI_PROVIDER=fake, and FakeAI puts the fake in place of a provider an Env option chose. A request with no reply left fails. Several FakeAI options add up; App.AI adds more during the test.

app := anetostest.New(t, setup, anetostest.FakeAI(
	ai.FakeToolCall("find_order", map[string]int{"number": 1042}),
	ai.FakeText("Order 1042 shipped yesterday."),
))

func FakeEvents added in v0.2.0

func FakeEvents(events ...any) Option

FakeEvents keeps events of the types of the given values (all events, with none) from reaching their listeners (a pointer type and its value type are different event types): they are only recorded, for AssertEmitted and Events. Without it, events are recorded and delivered as usual.

app := anetostest.New(t, setup, anetostest.FakeEvents(OrderPlaced{}))

func FakePubSub added in v0.2.0

func FakePubSub() Option

FakePubSub keeps published messages from reaching the broker: they are only recorded, for AssertPublished and Messages. Without it, messages are recorded and published as usual.

func FakeQueue added in v0.2.0

func FakeQueue() Option

FakeQueue keeps dispatched jobs from running or being stored: they are only recorded, for AssertDispatched and Jobs. Without it, jobs are recorded and handled by the queue as usual (QUEUE_DRIVER: with sync, they run at once). Queued email and queued event listeners are jobs too.

func FakeSocial added in v0.2.0

func FakeSocial() Option

FakeSocial makes social login (package auth/social) sign in through a stand-in provider the test controls, for every provider the app has: App.SocialSignIn signs in with an account of your choosing. Every provider counts as configured (social.Configured), with test credentials when its SOCIAL_<NAME>_* settings are missing. Nothing reaches Google or GitHub.

app := anetostest.New(t, setup, anetostest.FakeSocial())
app.SocialSignIn("/auth/google/redirect", anetostest.SocialAccount{
	ID: "g-1", Email: "ada@example.com", EmailVerified: true, Name: "Ada",
}).AssertRedirect("/dashboard")

func LogLevel

func LogLevel(l slog.Level) Option

LogLevel sets the minimum level of the app's logs, which go to the test's log (shown for failed tests and with -v). Default Info.

func WithoutMigrations

func WithoutMigrations() Option

WithoutMigrations skips running the app's migrations.

func WithoutTransaction

func WithoutTransaction() Option

WithoutTransaction lets the test's writes be committed (with PostgreSQL, MySQL or a SQLite file), for code that needs its own connections or AfterCommit callbacks. The test must clean up after itself.

type Response

type Response struct {
	StatusCode int           // the status code
	Header     http.Header   // the response headers
	Body       []byte        // the whole body
	Request    *http.Request // the request that was sent
	// contains filtered or unexported fields
}

Response is the app's response to a test request. Its assertion methods report failures with t.Errorf and return the response, so they chain:

app.Get("/posts").AssertOK().AssertSee("Hello")

func (*Response) AssertCreated

func (r *Response) AssertCreated() *Response

AssertCreated checks for 201 Created.

func (*Response) AssertDontSee

func (r *Response) AssertDontSee(texts ...string) *Response

AssertDontSee checks that the body contains none of texts, as they are or HTML-escaped.

func (*Response) AssertForbidden

func (r *Response) AssertForbidden() *Response

AssertForbidden checks for 403 Forbidden.

func (*Response) AssertHeader

func (r *Response) AssertHeader(name, want string) *Response

AssertHeader checks that the response has the header with the value.

func (*Response) AssertJSON

func (r *Response) AssertJSON(want any) *Response

AssertJSON checks that the body is JSON equal to want (encoded as JSON to compare): a struct, a map or a slice.

res.AssertJSON(map[string]any{"id": 1, "title": "Hello"})

func (*Response) AssertJSONPath

func (r *Response) AssertJSONPath(path string, want any) *Response

AssertJSONPath checks the value at path in the JSON body: keys and array indexes separated by dots, as in "data.0.title". want is compared after encoding it as JSON, so 1 matches 1.0 and a struct matches an object.

func (*Response) AssertNoContent

func (r *Response) AssertNoContent() *Response

AssertNoContent checks for 204 No Content.

func (*Response) AssertNoValidationErrors

func (r *Response) AssertNoValidationErrors() *Response

AssertNoValidationErrors checks that the request didn't fail validation: not a 422, no 400 with field errors, and no errors flashed to the session.

func (*Response) AssertNotFound

func (r *Response) AssertNotFound() *Response

AssertNotFound checks for 404 Not Found.

func (*Response) AssertOK

func (r *Response) AssertOK() *Response

AssertOK checks for 200 OK.

func (*Response) AssertRedirect

func (r *Response) AssertRedirect(path string) *Response

AssertRedirect checks for a redirect (3xx) to path, such as "/posts" or "/posts?page=2".

func (*Response) AssertRedirectRoute

func (r *Response) AssertRedirectRoute(name string, args ...any) *Response

AssertRedirectRoute checks for a redirect to the named route, with args as for web.Router.URL.

func (*Response) AssertSee

func (r *Response) AssertSee(texts ...string) *Response

AssertSee checks that the body contains each text, as it is or HTML-escaped as templates write it: AssertSee(`"Go" & you`) also finds "&#34;Go&#34; &amp; you". For JSON, use Response.AssertJSONPath.

func (*Response) AssertSessionHas

func (r *Response) AssertSessionHas(key string, value ...any) *Response

AssertSessionHas checks that the session the next request will carry has key and, if a value is given, that it holds that value (compared as JSON).

res.AssertSessionHas("status", "Post created.")

func (*Response) AssertSessionMissing

func (r *Response) AssertSessionMissing(key string) *Response

AssertSessionMissing checks that the session the next request will carry doesn't have key.

func (*Response) AssertStatus

func (r *Response) AssertStatus(want int) *Response

AssertStatus checks the status code.

func (*Response) AssertUnprocessable

func (r *Response) AssertUnprocessable() *Response

AssertUnprocessable checks for 422 Unprocessable Content, the status of failed validation for API clients.

func (*Response) AssertValidationErrors

func (r *Response) AssertValidationErrors(fields ...string) *Response

AssertValidationErrors checks that validation failed with an error for each of fields (the form's or the JSON body's field names; none: any error). For an API client, the response must be a 422 (or 400) problem with the fields in its "errors" member; for a browser's form, a redirect back with the errors flashed to the session.

func (*Response) Follow

func (r *Response) Follow() *Response

Follow sends a GET request to the redirect's Location, as a browser does. The response must be a redirect.

func (*Response) JSON

func (r *Response) JSON(v any) *Response

JSON decodes the body into v, failing the test if it can't.

func (*Response) Text

func (r *Response) Text() string

Text returns the body as a string.

type SocialAccount added in v0.2.0

type SocialAccount struct {
	// ID is the account's identifier at the provider (the profile's
	// Subject). Required.
	ID string
	// Email is the account's address.
	Email string
	// EmailVerified says the provider verified Email.
	EmailVerified bool
	// Name is the account's display name.
	Name string
	// AvatarURL is the account's picture.
	AvatarURL string
}

SocialAccount is the account a test signs in with at the stand-in provider (App.SocialSignIn): the profile the app's resolver gets.

type Upload added in v0.2.0

type Upload struct {
	// Field is the form field's name.
	Field string
	// Filename is the file's name, as the client sends it.
	Filename string
	// Content is the file's content.
	Content []byte
	// ContentType is the part's media type. Default
	// application/octet-stream.
	ContentType string
}

Upload is a file for App.PostMultipart.

Jump to

Keyboard shortcuts

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