Documentation

Index

Constants

This section is empty.

Variables

var ErrNotFound = errors.New("error key not found")

Functions

func AddMacro

func AddMacro(name string, macro interface{})

AddMacro inserts the provided macro in the map where they are held. It is not expected to be called concurrently.

func DisableJWS

func DisableJWS()

DisableJWS - disable jws-signature for ozone

func EnableContextDumps

func EnableContextDumps()

func ExecuteMacro

func ExecuteMacro(name string, params []string) (string, error)

ExecuteMacro calls a macro by `name`, with parameters to be passed using `params`. `params` is a collection of strings that get passed as is. Type assertions will need be performed in the macro implementation.

func JWSStatus

func JWSStatus() string

func NewDefaultPermissionGroup

func NewDefaultPermissionGroup(tc TestCase) permissions.Group

func NewPermissionGroup

func NewPermissionGroup(tc TestCase) permissions.Group

NewPermissionGroup returns a list of Code objects associated with a testcase

Types

type Code

type Code string

Code is a string representing a OB access permission

type Component

type Component struct {
	ID               string            `json:"@id,omitempty"`              // JSONLD ID Reference
	Name             string            `json:"name,omitempty"`             // Name
	Description      string            `json:"description,omitempty"`      // Purpose of the testcase in simple words
	Documentation    string            `json:"documentation,omitempty"`    // What input parameters do, what output parameters are
	InputParameters  map[string]string `json:"inputParameters,omitempty"`  // input parameters
	OutputParameters map[string]string `json:"outputParameters,omitempty"` // output parameters
	Tests            []TestCase        `json:"testcases,omitempty"`        // TestCase to be run as part of this custom test
	Execution        []string          `json:"execution,omitempty"`
	Components       []string          `json:"components,omitempty"`
}

Component - a reusable test case building block

func LoadComponent

func LoadComponent(filename string) (Component, error)

LoadComponent - Utility to load Manifest Data Model containing all Rules, Tests and Conditions

func MakeComponent

func MakeComponent(name string) Component

MakeComponent -

func (*Component) GetTests

func (c *Component) GetTests() []TestCase

GetTests - returns the tests that need to be run for this component

func (*Component) ProcessReplacementFields

func (c *Component) ProcessReplacementFields(ctx *Context)

ProcessReplacementFields - performance context/testcase parameter substitution on component test cases before they are run

func (*Component) ValidateParameters

func (c *Component) ValidateParameters(ctx *Context) error

ValidateParameters - check that the components required input and output parameters are present in the supplied context

type ConditionEnum

type ConditionEnum int

ConditionEnum models endpoint conditionality based on: Account and Transaction API Specification - v3.1 - Section 4 Endpoints https://openbanking.atlassian.net/wiki/spaces/DZ/pages/937820271/Account+and+Transaction+API+Specification+-+v3.1#AccountandTransactionAPISpecification-v3.1-Endpoints Also see "Categorisation of Implementation Requirements" section of the following document https://openbanking.atlassian.net/wiki/spaces/DZ/pages/937656404/Read+Write+Data+API+Specification+-+v3.1#Read/WriteDataAPISpecification-v3.1-CategorisationofImplementationRequirements

const (
	// Mandatory - required
	Mandatory ConditionEnum = iota
	// Conditional on a regulatory requirement
	Conditional
	// Optional at the implementors discretion
	Optional
	// UndefinedCondition -
	UndefinedCondition
)

func GetConditionality

func GetConditionality(method, endpoint, specification string) (ConditionEnum, error)

GetConditionality - returns and indicator in the following for of the method/endpoint conditionality model.Mandatory - endpoint is Mandatory model.Conditional - endpoint is conditional model.Optional - endpoint is optional model.UndefineCondition - we don't recognise the endpoint

type Conditionality

type Conditionality struct {
	Condition ConditionEnum `json:"condition,omitempty"`
	Method    string        `json:"method,omitempty"`
	Endpoint  string        `json:"endpoint,omitempty"`
}

Conditionality - capture the conditionality of a method/endpoint

func GetEndpointConditionality

func GetEndpointConditionality(specification string) []Conditionality

GetEndpointConditionality - get a clone of `endpointConditionality` array for given specification identifier

type ConditionalityChecker

type ConditionalityChecker interface {
	IsPresent(method, endpoint string, specification string) (bool, error)
	IsOptional(method, endpoint string, specification string) (bool, error)
	IsMandatory(method, endpoint string, specification string) (bool, error)
	IsConditional(method, endpoint string, specification string) (bool, error)
	MissingMandatory(endpoints []Input, specification string) ([]Input, error)
}

ConditionalityChecker - interface to provide loose coupling between endpoint conditionality checks and invoking code

func NewConditionalityChecker

func NewConditionalityChecker() ConditionalityChecker

NewConditionalityChecker - returns implementation of ConditionalityChecker interface for checking endpoint conditionality

type Context

type Context map[string]interface{}

Context is intended to handle two types of object and make them available to various parts of the suite including testcases. The first set are objects created as a result of the discovery phase, which capture discovery model information like endpoints and conditional implementation indicators. The other set of data is information passed between a sequence of test cases, for example AccountId - extracted from the output of one testcase (/Accounts) and fed in as part of the input of another testcase for example (/Accounts/{AccountId}/transactions}

func (*Context) Delete

func (c *Context) Delete(delKey string)

Delete Key from Context

func (*Context) DumpContext

func (c *Context) DumpContext(text ...string)

DumpContext - send the contents of a context to a logger

func (Context) Get

func (c Context) Get(key string) (interface{}, bool)

Get the key form the Context map - currently assumes value converts easily to a string!

func (Context) GetBool

func (c Context) GetBool(key string) (bool, error)

GetBool get the bool value associated with key

func (Context) GetString

func (c Context) GetString(key string) (string, error)

GetString get the string value associated with key

func (Context) GetStringSlice

func (c Context) GetStringSlice(key string) ([]string, error)

GetStringSlice gets a slice of string from context

func (*Context) GetStrings

func (c *Context) GetStrings(text ...string) (map[string]string, error)

GetStrings - given a list of strings, returns a map of the strings values from context

func (*Context) IsSet

func (c *Context) IsSet(key string) bool

IsSet returns true if the key exists and is not set to zero value (nil or empty string)

func (Context) Put

func (c Context) Put(key string, value interface{})

Put a value indexed by 'key' into the context. The value can be any type

func (Context) PutContext

func (c Context) PutContext(ctx *Context)

PutContext - puts another context into this one

func (Context) PutMap

func (c Context) PutMap(mymap map[string]string)

PutMap of strings - into context

func (Context) PutString

func (c Context) PutString(key string, value string)

PutString Put a value indexed by 'key' into the context. The value can be any type

func (Context) PutStringSlice

func (c Context) PutStringSlice(key string, values []string)

PutStringSlice puts a slice of strings into context

type ContextAccessor

type ContextAccessor struct {
	Context *Context `json:"-"`
	Matches []Match  `json:"matches,omitempty"`
}

ContextAccessor - Manages access to matches for Put and Get value operations on a context

func (*ContextAccessor) AppErr

func (c *ContextAccessor) AppErr(msg string) error

AppErr - application level trace error msg

func (*ContextAccessor) AppMsg

func (c *ContextAccessor) AppMsg(msg string) string

AppMsg - application level trace

func (*ContextAccessor) PutValues

func (c *ContextAccessor) PutValues(tc *TestCase, ctx *Context) error

PutValues is used by the 'contextPut' directive and essentially collects a set of matches whose purpose is To select values to put in a context. All the matches in this section must have a name (of the target context variable), a description (so if things go wrong we can accurately report) and an operation which results in the a selection which is copied into the context variable Note: the initial interation of this will just implement the JSON pattern/field matcher

type Expect

type Expect struct {
	StatusCode       int  `json:"status-code,omitempty"`       // Http response code
	SchemaValidation bool `json:"schema-validation,omitempty"` // Flag to indicate if we need schema validation -
	// provides the ability to switch off schema validation
	Matches    []Match         `json:"matches,omitempty"`    // An array of zero or more match items which must be 'passed' for the testcase to succeed
	ContextPut ContextAccessor `json:"contextPut,omitempty"` // allows storing of test response fragments in context variables
}

Expect defines a structure for expressing testcase result expectations.

func (*Expect) Clone

func (e *Expect) Clone() Expect

Clone - preforms deep copy of expect object

type Input

type Input struct {
	Method          string            `json:"method,omitempty"`          // http Method that this test case uses
	Endpoint        string            `json:"endpoint,omitempty"`        // resource endpoint where the http object needs to be sent to get a response
	Headers         map[string]string `json:"headers,omitempty"`         // Allows for provision of specific http headers
	RemoveHeaders   []string          `json:"removeheaders,omitempty"`   // Allows for removing specific http headers
	FormData        map[string]string `json:"formData,omitempty"`        // Allow for provision of http form data
	QueryParameters map[string]string `json:"queryParameters,omitempty"` // Allow for provision of http URL query parameters
	RequestBody     string            `json:"bodyData,omitempty"`        // Optional request body raw data
	Generation      map[string]string `json:"generation,omitempty"`      // Allows for different ways of generating testcases
	Claims          map[string]string `json:"claims,omitempty"`          // collects claims for input strategies that require them
	JwsSig          bool              `json:"jws,omitempty"`             // controls inclusion of x-jws-signature header
	IdempotencyKey  bool              `json:"idempotency,omitempty"`     // specifices the inclusion of x-idempotency-key in the request
}

Input defines the content of the http request object used to execute the test case Input is built up typically from the openapi/swagger definition of the method/endpoint for a particular specification. Additional properties/fields/headers can be added or change in order to setup the http request object of the specific test case. Once setup correctly,the testcase gives the http request object to the parent Rule which determine how to execute the requestion object. On execution an http response object is received and passed back to the testcase for validation using the Expects object.

func (*Input) AppErr

func (i *Input) AppErr(msg string) error

AppErr - application level trace error msg

func (*Input) AppMsg

func (i *Input) AppMsg(msg string) string

AppMsg - application level trace

func (*Input) Clone

func (i *Input) Clone() Input

Clone an Input

func (*Input) CreateRequest

func (i *Input) CreateRequest(tc *TestCase, ctx *Context) (*resty.Request, error)

CreateRequest is the main Input work horse which examines the various Input parameters and generates an http.Request object which represents the request

func (*Input) GenerateRequestToken

func (i *Input) GenerateRequestToken(ctx *Context) (string, error)

func (*Input) SetFormField

func (i *Input) SetFormField(key, value string)

SetFormField - sets a field in the form

func (*Input) SetHeader

func (i *Input) SetHeader(key, value string)

SetHeader - on the testcase input object

func (*Input) String

func (i *Input) String() string

String - object represetation

type Manifest

type Manifest struct {
	Context     string    `json:"@context"`         // JSONLD contest reference
	ID          string    `json:"@id"`              // JSONLD ID reference
	Type        string    `json:"@type"`            // JSONLD Type reference
	Name        string    `json:"name"`             // Name of the manifest
	Description string    `json:"description"`      // Description of the Manifest and what it contains
	BaseIri     string    `json:"baseIri"`          // Base Iri
	Sections    []Context `json:"section_contexts"` // Section specific contexts
	Rules       []Rule    `json:"rules"`            // All the rules in the Manifest
}

Manifest is the high level container for test suite definition It contains a list of all the rules required to be passed for conformance testing Each rule can have multiple testcases which contribute to testing that particular rule So essentially Manifest is a container

func (*Manifest) String

func (m *Manifest) String() string

type Match

type Match struct {
	MatchType       MatchType `json:"match_type,omitempty"`        // Type of Match we're doing
	Description     string    `json:"description,omitempty"`       // Description of the purpose of the match
	ContextName     string    `json:"name,omitempty"`              // Context variable name
	Header          string    `json:"header,omitempty"`            // Header value to examine
	HeaderPresent   string    `json:"header-present,omitempty"`    // Header existence check
	Regex           string    `json:"regex,omitempty"`             // Regular expression to be used
	JSON            string    `json:"json,omitempty"`              // Json expression to be used
	Value           string    `json:"value,omitempty"`             // Value to match against (string)
	Numeric         int64     `json:"numeric,omitempty"`           // Value to match against - numeric
	Count           int64     `json:"count,omitempty"`             // Cont for JSON array match purposes
	BodyLength      *int64    `json:"body-length,omitempty"`       // Body payload length for matching
	ReplaceEndpoint string    `json:"replaceInEndpoint,omitempty"` // allows substitution of resourceIds
	Authorisation   string    `json:"authorisation,omitempty"`     // allows capturing of bearer tokens
	Result          string    `json:"result,omitempty"`            // capturing match values
	Custom          string    `json:"custom,omitempty"`            // specifies custom matching routine
}

Match defines various types of response payload pattern and field checking. Match forms the basis for response validation outside of basic swagger/openapi schema validation Match is also used as the basis for field extraction and replacement which enable parameter passing between tests via the context. Match encapsulates a conditional statement that must 'match' in order to succeed. Matches can - - match a specified header field value for exact match - match a specified header field value using a regular expression - check that a specified header field exists in the response - check that a response body matches a regular expression - check that a response body has a particular json field present using json matching - check that a response body has a specific number of specified json array fields - check that a response body has a specific value of a specified json field - check that a response body has a specific json field and that the specific json field matches a regular expression - check that a response body is a specified length - allow for replacement of endpoint text ... e.g. {AccountId} - Authorization: allow for manipulation of Bearer tokens in http headers - Result: allow for capturing of match values for further processing - like putting into a context

func (*Match) AppErr

func (m *Match) AppErr(msg string) error

AppErr - application level trace error msg

func (*Match) AppMsg

func (m *Match) AppMsg(msg string) string

AppMsg - application level trace

func (*Match) Check

func (m *Match) Check(tc *TestCase) (bool, error)

Check a match function - figures out which match type we have and calls the appropriate match checking function

func (*Match) Clone

func (m *Match) Clone() Match

Clone duplicates a Match into a separate independent object TODO: consider cloning the contextPut Omit bodylength for now...

func (*Match) GetType

func (m *Match) GetType() MatchType

GetType - returns the type of a match

func (*Match) ProcessReplacementFields

func (m *Match) ProcessReplacementFields(ctx *Context)

ProcessReplacementFields allows parameter replacement within match string fields

func (*Match) PutValue

func (m *Match) PutValue(tc *TestCase, ctx *Context) bool

PutValue puts the value from the json match along with a context variable to put it into

func (*Match) String

func (m *Match) String() string

type MatchType

type MatchType int

MatchType enumeration

const (
	UnknownMatchType MatchType = iota
	HeaderValue
	HeaderRegex
	HeaderRegexContext
	HeaderPresent
	BodyRegex
	BodyJSONPresent
	BodyJSONCount
	BodyJSONValue
	BodyJSONRegex
	BodyLength
	Authorisation
	CustomCheck
)

MatchType enumeration - this will be required when we extend to more than just BodyJSONValue

type NamedPermission

type NamedPermission struct {
	Name       string                    `json:"name"`
	CodeSet    permissions.CodeSetResult `json:"codeSet"`
	ConsentUrl string                    `json:"consentUrl"`
}

type NamedPermissions

type NamedPermissions []NamedPermission

func (*NamedPermissions) Add

func (t *NamedPermissions) Add(token NamedPermission)

type ResourceAccountID

type ResourceAccountID struct {
	AccountID string `json:"account_id"`
}

type ResourceIDs

type ResourceIDs struct {
	AccountIDs   []ResourceAccountID   `json:"account_ids"`
	StatementIDs []ResourceStatementID `json:"statement_ids"`
}

type ResourceStatementID

type ResourceStatementID struct {
	StatementID string `json:"statement_id"`
}

type Rule

type Rule struct {
	ID           string       `json:"@id"`             // JSONLD ID reference
	Type         []string     `json:"@type,omitempty"` // JSONLD type reference
	Name         string       `json:"name"`            // A short meaningful name for this rule
	Purpose      string       `json:"purpose"`         // The purpose of this rule
	Specref      string       `json:"specref"`         // Description of area of spec/name/version/section under test
	Speclocation string       `json:"speclocation"`    // specific http reference to location in spec under test covered by this rule
	Tests        [][]TestCase `json:"tests"`           // Tests - allows for many testcases - array of arrays - to be associated with this rule
}

Rule - Define a specific location within a specification that is being tested Rule also identifies all the tests that must be passed in order to show that the rule implementation in conformant with the specific section in the referenced specification

func (*Rule) String

func (r *Rule) String() string

type SpecConsentRequirements

type SpecConsentRequirements struct {
	Identifier       string           `json:"specIdentifier"`
	NamedPermissions NamedPermissions `json:"namedPermissions"`
}

func NewSpecConsentRequirements

func NewSpecConsentRequirements(nameGenerator names.Generator, result permissions.CodeSetResultSet, specId string) SpecConsentRequirements

type Specification

type Specification struct {
	Identifier string
	Name       string
	// URL of confluence specifications file.
	URL *url.URL
	// Version of the specifications
	Version string
	// URL of OpenAPI/Swagger specifications file.
	SchemaVersion *url.URL
}

Specification - Represents OB API specification. Fields are from the APIReference JSON-LD schema, see: https://schema.org/APIReference

func SpecificationFromSchemaVersion

func SpecificationFromSchemaVersion(schemaVersion string) (Specification, error)

SpecificationFromSchemaVersion - returns specification struct for given schema version URL, or nil when there is no match.

func Specifications

func Specifications() []Specification

Specifications - get a clone of the `specifications` array.

type TestCase

type TestCase struct {
	ID                string           `json:"@id,omitempty"`                  // JSONLD ID Reference
	Type              []string         `json:"@type,omitempty"`                // JSONLD type array
	Name              string           `json:"name,omitempty"`                 // Name
	Detail            string           `json:"detail,omitempty"`               // Detailed description of the test case
	RefURI            string           `json:"refURI,omitempty"`               // Reference URI for the test case
	Purpose           string           `json:"purpose,omitempty"`              // Purpose of the testcase in simple words
	Input             Input            `json:"input,omitempty"`                // Input Object
	Context           Context          `json:"context,omitempty"`              // Local Context Object
	Expect            Expect           `json:"expect,omitempty"`               // Expected object
	ExpectOneOf       []Expect         `json:"expect_one_of,omitempty"`        // Slice of possible expected objects
	ParentRule        *Rule            `json:"-"`                              // Allows accessing parent Rule
	Request           *resty.Request   `json:"-"`                              // The request that's been generated in order to call the endpoint
	Header            http.Header      `json:"-"`                              // ResponseHeader
	Body              string           `json:"-"`                              // ResponseBody
	Bearer            string           `json:"bearer,omitempty"`               // Bear token if presented
	DoNotCallEndpoint bool             `json:"do_not_call_endpoint,omitempty"` // If we should not call the endpoint, see `components/PSUConsentProviderComponent.json`
	APIName           string           `json:"apiName"`
	APIVersion        string           `json:"apiVersion"`
	Validator         schema.Validator `json:"-"` // Swagger schema validator
	ValidateSignature bool             `json:"validateSignature,omitempty"`
	StatusCode        string           `json:"statusCode,omitempty"`
}

TestCase defines a test that will be run and needs to be passed as part of the conformance suite in order to determine implementation conformance to a specification. Testcase have three major sections Input:

Defines the inputs that are required by the testcase. This effectively involves preparing the http request object

Context:

Provides a link between Discovery information and the testcase

Expects:

Examines the http response to the testcase Input in order to determine if the expected conditions existing in the response
and therefore the testcase has passed

func LoadTestCaseFromJSONFile

func LoadTestCaseFromJSONFile(filename string) (TestCase, error)

LoadTestCaseFromJSONFile a single testcase from a json file

func MakeTestCase

func MakeTestCase() TestCase

MakeTestCase builds an empty testcase

func (*TestCase) AppEntry

func (t *TestCase) AppEntry(msg string) string

AppEntry - application level trace error msg

func (*TestCase) AppErr

func (t *TestCase) AppErr(msg string) error

AppErr - application level trace error msg

func (*TestCase) AppExit

func (t *TestCase) AppExit(msg string) string

AppExit - application level trace error msg

func (*TestCase) AppMsg

func (t *TestCase) AppMsg(msg string) string

AppMsg - application level trace

func (*TestCase) ApplyContext

func (t *TestCase) ApplyContext(rulectx *Context)

ApplyContext - at the end of ApplyInputs on the testcase - we have an initial http request object ApplyContext, applies context parameters to the http object. Context parameter typically involve variables that originated in discovery The functionality of ApplyContext will grow significantly over time.

func (*TestCase) ApplyExpects

func (t *TestCase) ApplyExpects(res *resty.Response, rulectx *Context) (bool, []error)

ApplyExpects runs the Expects section of the testcase to evaluate if the response from the system under test passes or fails The Expects section of a testcase can contain multiple conditions that need to be met to pass a testcase When a test fails, ApplyExpects is responsible for reporting back information about the failure, why it occurred, where it occurred etc.

The ApplyExpect section is also responsible for running and contextPut clauses. contextPuts are responsible for updated context variables with values selected from the test case response contextPuts will only be executed if the ApplyExpects standards match tests pass if any of the ApplyExpects match tests fail - ApplyExpects returns false and contextPuts aren't executed

func (*TestCase) ApplyInput

func (t *TestCase) ApplyInput(rulectx *Context) (*resty.Request, error)

ApplyInput - creates an HTTP request for this test case The reason why we're doing this is that a testcase behaves like an http object It produces an http.Request - which can be sent to a server It consumes an http.Response - which it uses to validate the response against "Expects" TestCase lifecycle:

Create a Testcase Object
Create / retrieve the http request object
Apply context information to the request object
Rule - manages passing the request object from the testcase to an appropriate endpoint handler (like the proxy)
Rule - receives http response from endpoint and provides it back to testcase
Testcase evaluates the http response object using its 'Expects' clause
Testcase passes or fails depending on the 'Expects' outcome

func (*TestCase) Clone

func (t *TestCase) Clone() TestCase

Clone a testcase

func (*TestCase) InjectBearerToken

func (t *TestCase) InjectBearerToken(token string)

InjectBearerToken injects a bear token header into the testcase, token can either be the actual bearer token or a parameter starting with '$'

func (*TestCase) Prepare

func (t *TestCase) Prepare(ctx *Context) (*resty.Request, error)

Prepare a Testcase for execution at and endpoint, results in a standard http request that encapsulates the testcase request as defined in the test case object with any context inputs/replacements etc applied

func (*TestCase) ProcessReplacementFields

func (t *TestCase) ProcessReplacementFields(ctx *Context, showReplacementErrors bool)

ProcessReplacementFields prefixed by '$' in the testcase Input and Context sections Call to pre-process custom test cases from discovery model

func (*TestCase) String

func (t *TestCase) String() string

func (*TestCase) Validate

func (t *TestCase) Validate(resp *resty.Response, ctx *Context) (bool, []error)

Validate takes the http response that results as a consequence of sending the testcase http request to the endpoint implementation. Validate is responsible for checking the http status code and running the set of 'Matches' within the 'Expect' object, to determine if all the match conditions are met - which would mean the validation passed. The context object is passed as part of the validation as its allows the match clauses to examine the request object and 'push' response variables into the context object for use in downstream test cases which are potentially part of this testcase sequence returns true - validation successful

false - validation unsuccessful
error - adds detail to validation failure
NOTE: Validate will only return false if a check fails - no checks = true