78 lines
2.7 KiB
Go
78 lines
2.7 KiB
Go
|
|
package behaviour
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/url"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
// neturlValues builds a form body from plain strings, so phases do not have to
|
||
|
|
// repeat the url.Values literal for every field.
|
||
|
|
func neturlValues(fields map[string]string) url.Values {
|
||
|
|
values := url.Values{}
|
||
|
|
for name, value := range fields {
|
||
|
|
values.Set(name, value)
|
||
|
|
}
|
||
|
|
return values
|
||
|
|
}
|
||
|
|
|
||
|
|
// tenantPhases exercises the installation-wide tenant management, which is the
|
||
|
|
// one console action restricted to system administrators.
|
||
|
|
func tenantPhases(t *testing.T, j *journey) {
|
||
|
|
t.Helper()
|
||
|
|
|
||
|
|
phase(t, "a system administrator creates a tenant", func(t *testing.T) {
|
||
|
|
type tenant struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
Slug string `json:"slug"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
}
|
||
|
|
response := j.admin.json(http.MethodPost, j.adminHost(), "/api/v1/tenants", map[string]string{
|
||
|
|
"slug": "behaviour", "name": "Behaviour Tenant",
|
||
|
|
})
|
||
|
|
created := decode[tenant](t, response, http.StatusCreated)
|
||
|
|
if created.Slug != "behaviour" {
|
||
|
|
t.Errorf("slug = %q", created.Slug)
|
||
|
|
}
|
||
|
|
if created.ID == "" {
|
||
|
|
t.Error("no tenant id returned")
|
||
|
|
}
|
||
|
|
// Later phases build their own application in this tenant, so it isolates
|
||
|
|
// authorization from the seeded demonstration tenant.
|
||
|
|
j.scopedTenant, j.scopedTenantID = created.Slug, created.ID
|
||
|
|
})
|
||
|
|
|
||
|
|
phase(t, "a duplicate tenant slug is refused", func(t *testing.T) {
|
||
|
|
response := j.admin.json(http.MethodPost, j.adminHost(), "/api/v1/tenants", map[string]string{
|
||
|
|
"slug": "behaviour", "name": "Duplicate",
|
||
|
|
})
|
||
|
|
statusOnly(t, response, http.StatusConflict)
|
||
|
|
})
|
||
|
|
|
||
|
|
phase(t, "a malformed tenant slug is refused", func(t *testing.T) {
|
||
|
|
for _, slug := range []string{"Has Uppercase", "has/slash", "-leading", ""} {
|
||
|
|
response := j.admin.json(http.MethodPost, j.adminHost(), "/api/v1/tenants", map[string]string{
|
||
|
|
"slug": slug, "name": "Invalid",
|
||
|
|
})
|
||
|
|
statusOnly(t, response, http.StatusBadRequest)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
phase(t, "a tenant administrator cannot create a tenant", func(t *testing.T) {
|
||
|
|
// The seeded NMBS interviewer is a tenant administrator, not a system one.
|
||
|
|
browser := j.browser()
|
||
|
|
host := j.adminHost()
|
||
|
|
token := csrfFrom(t, bodyOf(t, browser.get(host, "/login", nil)))
|
||
|
|
response := browser.form(host, "/auth/password", neturlValues(map[string]string{
|
||
|
|
"csrf_token": token, "email": "interview@nmbs.auth.dev", "password": "admin123",
|
||
|
|
}), nil)
|
||
|
|
if response.StatusCode != http.StatusSeeOther {
|
||
|
|
t.Fatalf("seeded administrator sign-in = %d: %s", response.StatusCode, bodyOf(t, response))
|
||
|
|
}
|
||
|
|
response = browser.json(http.MethodPost, host, "/api/v1/tenants", map[string]string{
|
||
|
|
"slug": "unauthorised", "name": "Unauthorised",
|
||
|
|
})
|
||
|
|
statusOnly(t, response, http.StatusForbidden)
|
||
|
|
})
|
||
|
|
}
|