Documentation
¶
Index ¶
- Constants
- Variables
- func FmtStatus(what string, got, want int) string
- func GetString(m map[string]any, keys ...string) string
- func GetStringSlice(m map[string]any, key string) []string
- func HasSchema(body map[string]any, uri string) bool
- func IDOf(body map[string]any) string
- func IsBase64(s string) bool
- func IsSchemaURI(s string) bool
- func IsValidAttrName(s string) bool
- func RFCText(rfc int) (string, error)
- func RandomSuffix() string
- func SectionFile(rfc, startLine, endLine int) (path string, localStart, localEnd int)
- type DiscoveredExtension
- type DiscoveredResourceType
- type Feature
- type Level
- type Requirement
- type Run
- func (r *Run) Check(ok bool, msg string)
- func (r *Run) Cleanup(fn func())
- func (r *Run) CreateFuzzedResource(rt DiscoveredResourceType, opts ...fuzz.Option) (map[string]any, *scim.Response)
- func (r *Run) CreateGroup(members []map[string]any) (map[string]any, *scim.Response)
- func (r *Run) CreateTestResource(extra map[string]any) (map[string]any, *scim.Response)
- func (r *Run) CreateUser() (map[string]any, *scim.Response)
- func (r *Run) DiscoverResourceTypes() []DiscoveredResourceType
- func (r *Run) DoWithHeaders(method, path string, body map[string]any, headers map[string]string) *scim.Response
- func (r *Run) Errorf(format string, args ...any)
- func (r *Run) Execute(fn func(r *Run))
- func (r *Run) Failed() bool
- func (r *Run) Fatalf(format string, args ...any)
- func (r *Run) GenerateSelfSignedCertB64() string
- func (r *Run) Logf(format string, args ...any)
- func (r *Run) Logs() []string
- func (r *Run) Messages() []string
- func (r *Run) RawClient() *scim.Client
- func (r *Run) RequireOK(err error)
- func (r *Run) RunCleanups()
- func (r *Run) Skipf(format string, args ...any)
- func (r *Run) Skipped() bool
- func (r *Run) SubResults() []SubResult
- func (r *Run) Subtest(name string, fn func(r *Run))
- type Source
- type SubResult
- type Test
Constants ¶
const ( UserSchema = "urn:ietf:params:scim:schemas:core:2.0:User" GroupSchema = "urn:ietf:params:scim:schemas:core:2.0:Group" TestResourceSchema = "urn:ietf:params:scim:schemas:test:2.0:TestResource" TestExtSchema = "urn:ietf:params:scim:schemas:extension:test:2.0:TestResource" PatchOpSchema = "urn:ietf:params:scim:api:messages:2.0:PatchOp" BulkSchema = "urn:ietf:params:scim:api:messages:2.0:BulkRequest" )
Variables ¶
var All []Requirement
All contains every cataloged requirement across all three RFCs.
var Extra = []Requirement{ { ID: "EXTRA-TYPE-INT", Level: Must, Summary: "Integer attributes must round-trip without fractional parts", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "integer_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "integerAttr": 42, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST /TestResources returned %d", resp.StatusCode) } v, ok := body["integerAttr"].(float64) r.Check(ok && v == 42, fmt.Sprintf("integerAttr = %v, want 42", body["integerAttr"])) id := IDOf(body) getResp, err := r.Client.Get("/TestResources/" + id) r.RequireOK(err) if getResp.StatusCode == 200 { gv, _ := getResp.Body["integerAttr"].(float64) r.Check(gv == float64(int(gv)), fmt.Sprintf("integerAttr on GET = %v, contains fractional part", gv)) } }, }, }, }, { ID: "EXTRA-TYPE-BOOL", Level: Must, Summary: "Boolean attributes must round-trip as JSON true/false", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "boolean_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "booleanAttr": true, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } v, ok := body["booleanAttr"].(bool) r.Check(ok && v, fmt.Sprintf("booleanAttr = %v (%T), want true", body["booleanAttr"], body["booleanAttr"])) }, }, }, }, { ID: "EXTRA-TYPE-DT", Level: Must, Summary: "DateTime attributes must round-trip as valid xsd:dateTime strings", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "datetime_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "dateTimeAttr": "2025-06-15T10:30:00Z", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } v, ok := body["dateTimeAttr"].(string) r.Check(ok && strings.Contains(v, "T") && (strings.HasSuffix(v, "Z") || len(v) > 19), fmt.Sprintf("dateTimeAttr = %q, want valid xsd:dateTime", v)) }, }, }, }, { ID: "EXTRA-TYPE-BIN", Level: Must, Summary: "Binary attributes must round-trip as base64-encoded strings", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "binary_round_trip", Fn: func(r *Run) { raw := []byte("hello, compliance test") encoded := base64.StdEncoding.EncodeToString(raw) body, resp := r.CreateTestResource(map[string]any{ "binaryAttr": encoded, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } v, ok := body["binaryAttr"].(string) r.Check(ok && v != "", fmt.Sprintf("binaryAttr = %v, want base64 string", body["binaryAttr"])) if ok && v != "" { _, err := base64.StdEncoding.DecodeString(v) r.Check(err == nil, fmt.Sprintf("binaryAttr is not valid base64: %v", err)) } }, }, }, }, { ID: "EXTRA-TYPE-REF", Level: Must, Summary: "Reference attributes must round-trip as URI strings", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "reference_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "referenceAttr": "https://example.com/resource/123", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } v, ok := body["referenceAttr"].(string) r.Check(ok && v != "", fmt.Sprintf("referenceAttr = %v, want URI string", body["referenceAttr"])) if ok && v != "" { _, err := url.Parse(v) r.Check(err == nil, fmt.Sprintf("referenceAttr is not a valid URI: %v", err)) } }, }, }, }, { ID: "EXTRA-TYPE-COMPLEX", Level: Must, Summary: "Complex attributes must round-trip with sub-attributes intact", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "complex_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "complexAttr": map[string]any{ "sub1": "hello", "sub2": 99, "sub3": true, }, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } c, ok := body["complexAttr"].(map[string]any) r.Check(ok && c != nil, fmt.Sprintf("complexAttr = %v, want object", body["complexAttr"])) if ok && c != nil { s1, _ := c["sub1"].(string) r.Check(s1 == "hello", fmt.Sprintf("complexAttr.sub1 = %q, want \"hello\"", s1)) s2, _ := c["sub2"].(float64) r.Check(s2 == 99, fmt.Sprintf("complexAttr.sub2 = %v, want 99", c["sub2"])) } }, }, }, }, { ID: "EXTRA-MUT-IMMUTABLE", Level: Must, Summary: "Immutable attributes must be accepted at creation and rejected on update", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "immutable_attr", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "immutableAttr": "set-at-creation", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } id := IDOf(body) identifier, _ := body["identifier"].(string) v, _ := body["immutableAttr"].(string) r.Check(v == "set-at-creation", fmt.Sprintf("immutableAttr = %q after create, want \"set-at-creation\"", v)) putResp, err := r.Client.Put("/TestResources/"+id, map[string]any{ "schemas": []string{TestResourceSchema}, "identifier": identifier, "immutableAttr": "changed-value", }) r.RequireOK(err) if putResp.StatusCode == 200 { updated, _ := putResp.Body["immutableAttr"].(string) r.Check(updated == "set-at-creation", fmt.Sprintf("immutableAttr changed to %q on PUT, should stay immutable", updated)) } else { r.Check(putResp.StatusCode == 400, FmtStatus("PUT with changed immutable attr", putResp.StatusCode, 400)) } }, }, }, }, { ID: "EXTRA-MUT-WRITEONLY", Level: Must, Summary: "WriteOnly attributes must be accepted but never returned", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "write_only_attr", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "writeOnlyAttr": "secret-value", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } _, hasWO := body["writeOnlyAttr"] r.Check(!hasWO, "writeOnlyAttr was returned in POST response") id := IDOf(body) getResp, err := r.Client.Get("/TestResources/" + id) r.RequireOK(err) if getResp.StatusCode == 200 { _, hasWO = getResp.Body["writeOnlyAttr"] r.Check(!hasWO, "writeOnlyAttr was returned in GET response") } }, }, }, }, { ID: "EXTRA-MUT-READONLY", Level: Must, Summary: "ReadOnly attributes provided by the client must be ignored", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "read_only_ignored", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "readOnlyAttr": "client-value", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } v, _ := body["readOnlyAttr"].(string) r.Check(v != "client-value", fmt.Sprintf("readOnlyAttr = %q, server should have ignored client value", v)) }, }, }, }, { ID: "EXTRA-RET-ALWAYS", Level: Must, Summary: "Returned:always attributes must appear even when excluded", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "returned_always", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "alwaysReturnedAttr": "always-here", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } id := IDOf(body) getResp, err := r.Client.Get("/TestResources/" + id + "?excludedAttributes=alwaysReturnedAttr") r.RequireOK(err) if getResp.StatusCode == 200 { v, has := getResp.Body["alwaysReturnedAttr"] r.Check(has && v != nil, "returned:always attribute was excluded by excludedAttributes") } }, }, }, }, { ID: "EXTRA-RET-NEVER", Level: Must, Summary: "Returned:never attributes must never appear in responses", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "returned_never", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "writeOnlyAttr": "secret-value", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } _, hasWO := body["writeOnlyAttr"] r.Check(!hasWO, "writeOnlyAttr was returned in POST response") id := IDOf(body) getResp, err := r.Client.Get("/TestResources/" + id) r.RequireOK(err) if getResp.StatusCode == 200 { _, hasWO = getResp.Body["writeOnlyAttr"] r.Check(!hasWO, "writeOnlyAttr was returned in GET response") } }, }, }, }, { ID: "EXTRA-CASE-EXACT", Level: Must, Summary: "Filters on caseExact=true attributes must match case-sensitively", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "filter_case_exact", Fn: func(r *Run) { _, resp := r.CreateTestResource(map[string]any{ "caseExactString": "CaSeExAcT", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } filter := "caseExactString eq \"caseexact\"" qResp, err := r.Client.Get("/TestResources?filter=" + url.QueryEscape(filter)) r.RequireOK(err) if qResp.StatusCode == 200 { tr, _ := qResp.Body["totalResults"].(float64) r.Check(tr == 0, fmt.Sprintf("caseExact filter with wrong case matched %v results, want 0", tr)) } filter = "caseExactString eq \"CaSeExAcT\"" qResp2, err := r.Client.Get("/TestResources?filter=" + url.QueryEscape(filter)) r.RequireOK(err) if qResp2.StatusCode == 200 { tr, _ := qResp2.Body["totalResults"].(float64) r.Check(tr >= 1, fmt.Sprintf("caseExact filter with correct case matched %v results, want >= 1", tr)) } }, }, }, }, { ID: "EXTRA-CASE-INSENSITIVE", Level: Must, Summary: "Filters on caseExact=false attributes must match case-insensitively", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "filter_case_insensitive", Fn: func(r *Run) { _, resp := r.CreateTestResource(map[string]any{ "caseInsensitiveString": "MiXeDcAsE", }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } filter := "caseInsensitiveString eq \"mixedcase\"" qResp, err := r.Client.Get("/TestResources?filter=" + url.QueryEscape(filter)) r.RequireOK(err) if qResp.StatusCode == 200 { tr, _ := qResp.Body["totalResults"].(float64) r.Check(tr >= 1, fmt.Sprintf("case-insensitive filter matched %v results, want >= 1", tr)) } }, }, }, }, { ID: "EXTRA-EXT-CONTAINER", Level: Must, Summary: "Extension attributes must be namespaced under the extension schema URI", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "extension_container", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "schemas": []string{TestResourceSchema, TestExtSchema}, TestExtSchema: map[string]any{ "extString": "ext-value", "extRequired": "required-value", }, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } extData, ok := body[TestExtSchema].(map[string]any) r.Check(ok && extData != nil, fmt.Sprintf("extension data not found under key %q", TestExtSchema)) }, }, }, }, { ID: "EXTRA-EXT-ROUNDTRIP", Level: Must, Summary: "Extension attributes must round-trip through create and get", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "extension_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "schemas": []string{TestResourceSchema, TestExtSchema}, TestExtSchema: map[string]any{ "extString": "ext-value", "extRequired": "required-value", }, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } extData, ok := body[TestExtSchema].(map[string]any) if ok && extData != nil { es, _ := extData["extString"].(string) r.Check(es == "ext-value", fmt.Sprintf("extString = %q, want \"ext-value\"", es)) er, _ := extData["extRequired"].(string) r.Check(er == "required-value", fmt.Sprintf("extRequired = %q, want \"required-value\"", er)) } id := IDOf(body) getResp, err := r.Client.Get("/TestResources/" + id) r.RequireOK(err) if getResp.StatusCode == 200 { extGet, ok := getResp.Body[TestExtSchema].(map[string]any) r.Check(ok && extGet != nil, "GET: extension data missing after creation") } }, }, }, }, { ID: "EXTRA-EXT-SCHEMAS", Level: Must, Summary: "Resources with extensions must include extension URI in schemas array", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "extension_schemas", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "schemas": []string{TestResourceSchema, TestExtSchema}, TestExtSchema: map[string]any{ "extString": "ext-value", "extRequired": "required-value", }, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } schemas := GetStringSlice(body, "schemas") r.Check(HasSchema(body, TestExtSchema), fmt.Sprintf("schemas = %v, missing extension URI %s", schemas, TestExtSchema)) }, }, }, }, { ID: "EXTRA-MV-ROUNDTRIP", Level: Must, Summary: "Multi-valued attributes must round-trip preserving all values", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "multi_valued_round_trip", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "multiStrings": []string{"alpha", "beta", "gamma"}, }) if resp.StatusCode != 201 { r.Fatalf("setup: POST returned %d", resp.StatusCode) } arr, ok := body["multiStrings"].([]any) r.Check(ok && len(arr) == 3, fmt.Sprintf("multiStrings = %v, want 3 elements", body["multiStrings"])) }, }, }, }, { ID: "EXTRA-MV-COMPLEX-PRIMARY", Level: Must, Summary: "Multi-valued complex attributes must enforce at most one primary=true", Feature: TestResource, Testable: true, Tests: []Test{ { Name: "multi_complex_primary", Fn: func(r *Run) { body, resp := r.CreateTestResource(map[string]any{ "multiComplex": []map[string]any{ {"value": "one", "type": "a", "primary": true}, {"value": "two", "type": "b", "primary": true}, }, }) if resp.StatusCode == 201 { mc, _ := body["multiComplex"].([]any) primaryCount := 0 for _, item := range mc { m, ok := item.(map[string]any) if !ok { continue } if p, _ := m["primary"].(bool); p { primaryCount++ } } r.Check(primaryCount <= 1, fmt.Sprintf("multiComplex has %d primary=true, want <= 1", primaryCount)) } else if resp.StatusCode == 400 { r.Check(true, "") } else { r.Check(false, FmtStatus("POST /TestResources with duplicate primary", resp.StatusCode, 400)) } }, }, }, }, }
Extra contains additional compliance checks that go beyond the literal RFC requirements. These exercise attribute type combinations, extension handling, and edge cases that the standard User/Group schemas do not cover.
var RFC7642 = []Requirement{ { ID: "RFC7642-4-L944", Level: Must, Summary: "Transport layer must guarantee data confidentiality (TLS)", Source: Source{ RFC: 7642, Section: "4", StartLine: 943, EndLine: 945, }, Feature: Core, Testable: false, }, }
var RFC7643 = concat(
rfc7643_1_1,
rfc7643_2_1,
rfc7643_2_3,
rfc7643_2_4,
rfc7643_2_5,
rfc7643_3,
rfc7643_3_1,
rfc7643_3_3,
rfc7643_4_1,
rfc7643_5,
rfc7643_6,
rfc7643_7,
rfc7643_9,
)
RFC7643 contains requirements from RFC 7643.
var RFC7644 = concat(
rfc7644_1_3,
rfc7644_2,
rfc7644_3_1,
rfc7644_3_2,
rfc7644_3_3,
rfc7644_3_4,
rfc7644_3_5,
rfc7644_3_6,
rfc7644_3_7,
rfc7644_3_8,
rfc7644_3_9,
rfc7644_3_11,
rfc7644_3_12,
rfc7644_3_13,
rfc7644_3_14,
rfc7644_4,
rfc7644_5,
rfc7644_7,
)
RFC7644 contains requirements from RFC 7644.
Functions ¶
func GetStringSlice ¶
GetStringSlice extracts a []string from a JSON interface slice.
func IsSchemaURI ¶
IsSchemaURI returns true if the key is a SCIM schema URI (extension container key), not a regular attribute name.
func IsValidAttrName ¶
IsValidAttrName checks if a string conforms to the SCIM ATTRNAME ABNF: ALPHA *(nameChar) where nameChar = "-" / "_" / DIGIT / ALPHA.
func RFCText ¶
RFCText returns the full text of the given RFC, reassembled from per-section files. The result is byte-identical to the original monolithic file.
func RandomSuffix ¶
func RandomSuffix() string
RandomSuffix returns a short random hex string for unique test data.
func SectionFile ¶
SectionFile returns the testdata file path and local line numbers for a given RFC and global line range. This is used to generate source links that point to the correct section file.
Types ¶
type DiscoveredExtension ¶
DiscoveredExtension holds a parsed extension schema and whether it is required.
type DiscoveredResourceType ¶
type DiscoveredResourceType struct {
Name string
Endpoint string
Schema schema.Schema
Extensions []DiscoveredExtension
}
DiscoveredResourceType holds a resource type and its parsed schemas, as fetched from /ResourceTypes and /Schemas.
type Feature ¶
type Feature string
Feature groups requirements by optional server capability. Tests for a feature are skipped when the feature is not supported.
const ( // Core is always tested (required endpoints, basic CRUD, errors). Core Feature = "core" // Discovered via /ServiceProviderConfig. Filter Feature = "filter" Sort Feature = "sort" ChangePassword Feature = "changePassword" Patch Feature = "patch" Bulk Feature = "bulk" ETag Feature = "etag" // Resource types discovered via /ResourceTypes. Users Feature = "users" Groups Feature = "groups" // Custom test resource for exercising all attribute types. TestResource Feature = "testResource" // The /Me endpoint alias. Me Feature = "me" )
type Requirement ¶
type Requirement struct {
// ID is a stable identifier.
// Format: RFC{num}-{section}-L{line}, where {line} is the line
// containing the RFC 2119 keyword. This may differ from
// Source.StartLine when the sentence begins on an earlier line.
ID string
// Level is the RFC 2119 compliance level.
Level Level
// Summary is a short, test-oriented description of what to verify.
Summary string
// Source is the location in the RFC txt file.
Source Source
// Feature determines when this requirement is tested.
Feature Feature
// Testable indicates whether this can be verified black-box.
// Some requirements (e.g. internal password hashing) are not
// externally observable and are marked false.
Testable bool
// Tests contains the compliance tests for this requirement.
Tests []Test
}
Requirement is a single testable statement from the SCIM RFCs.
func ByFeature ¶
func ByFeature(f Feature) []Requirement
ByFeature returns all requirements that belong to the given feature.
func ByID ¶
func ByID(id string) *Requirement
ByID returns the requirement with the given ID, or nil if not found.
func ByLevel ¶
func ByLevel(l Level) []Requirement
ByLevel returns all requirements at the given compliance level.
type Run ¶
type Run struct {
Client *scim.Client
Features *scim.Features
// contains filtered or unexported fields
}
Run provides assertions, HTTP helpers, and lifecycle management for a single compliance test. It carries the SCIM client so that test functions defined in spec/ can make HTTP calls without importing any other package.
func (*Run) Cleanup ¶
func (r *Run) Cleanup(fn func())
Cleanup registers a function to be called after the test completes.
func (*Run) CreateFuzzedResource ¶
func (r *Run) CreateFuzzedResource(rt DiscoveredResourceType, opts ...fuzz.Option) (map[string]any, *scim.Response)
CreateFuzzedResource generates a resource from the schema using the fuzzer, POSTs it to the endpoint, and registers cleanup.
func (*Run) CreateGroup ¶
CreateGroup creates a Group and registers cleanup to delete it.
func (*Run) CreateTestResource ¶
CreateTestResource creates a TestResource and registers cleanup.
func (*Run) CreateUser ¶
CreateUser creates a test user and registers cleanup to delete it.
func (*Run) DiscoverResourceTypes ¶
func (r *Run) DiscoverResourceTypes() []DiscoveredResourceType
DiscoverResourceTypes fetches /ResourceTypes and /Schemas, parses the schemas, and returns structured resource type definitions.
func (*Run) DoWithHeaders ¶
func (r *Run) DoWithHeaders(method, path string, body map[string]any, headers map[string]string) *scim.Response
DoWithHeaders executes an HTTP request with additional headers.
func (*Run) Execute ¶
Execute runs a test function, recovering from Fatalf/Skipf panics and running cleanup functions afterward.
func (*Run) GenerateSelfSignedCertB64 ¶
GenerateSelfSignedCertB64 creates a self-signed X.509 certificate and returns it as a base64-encoded DER string.
func (*Run) RawClient ¶
RawClient returns a Client with no authentication, for testing unauthenticated access.
func (*Run) RunCleanups ¶
func (r *Run) RunCleanups()
RunCleanups executes all registered cleanup functions in LIFO order.
func (*Run) SubResults ¶
SubResults returns the collected subtest results.
type Source ¶
type Source struct {
// RFC number: 7642, 7643, or 7644.
RFC int
// Section identifier, e.g. "3.5.1".
Section string
// StartLine is the starting line number in the plain-text RFC file.
StartLine int
// EndLine is the ending line number (inclusive). Equal to StartLine
// when the requirement fits on a single line.
EndLine int
// StartCol is the 1-based column where highlighting begins on
// StartLine. Zero means highlight from the beginning of the line.
StartCol int
// EndCol is the 1-based column where highlighting ends (inclusive)
// on EndLine. Zero means highlight to the end of the line.
EndCol int
}
Source pinpoints where a requirement comes from in the RFC txt.
Source Files
¶
- all.go
- extra.go
- rfc7642.go
- rfc7643.go
- rfc7643_1_1.go
- rfc7643_2_1.go
- rfc7643_2_3.go
- rfc7643_2_4.go
- rfc7643_2_5.go
- rfc7643_3.go
- rfc7643_3_1.go
- rfc7643_3_3.go
- rfc7643_4_1.go
- rfc7643_5.go
- rfc7643_6.go
- rfc7643_7.go
- rfc7643_9.go
- rfc7644.go
- rfc7644_1_3.go
- rfc7644_2.go
- rfc7644_3_1.go
- rfc7644_3_11.go
- rfc7644_3_12.go
- rfc7644_3_13.go
- rfc7644_3_14.go
- rfc7644_3_2.go
- rfc7644_3_3.go
- rfc7644_3_4.go
- rfc7644_3_5.go
- rfc7644_3_6.go
- rfc7644_3_7.go
- rfc7644_3_8.go
- rfc7644_3_9.go
- rfc7644_4.go
- rfc7644_5.go
- rfc7644_7.go
- run.go
- spec.go
- testdata.go