check

package
v0.0.0-...-70b8fbd Latest Latest
Warning

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

Go to latest
Published: May 3, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package check provides validation helpers for ygnmi queries.

These helpers wrap the .Lookup() and .Watch()/.Await() methods of Ondatra's ygnmi structs, eliminating a great deal of boilerplate and allowing table-driven testing of paths of different types.

Overview

This package provides a set of functions for creating Validator objects, each of which represents a check to be performed against some gNMI path. A Validator can then be invoked in several different ways to actually perform the check. Typical usage looks like this:

// Create a table of Validators.
validators := []check.Validator{
	check.Equal(ocpath.Some().Path().State(), someValue),
	check.Present(ocpath.Some().OtherPath().Config()),
	check.NotEqual(ocpath.Another().Path().State(), anotherValue),
}
// Check each one and report any failures.
for _, vd:= range validators {
	t.Run(vd.Path(), func(t *testing.T) {
		if err := vd.Check(gnmiClient); err != nil {
			// err will already look like e.g. "some/path/state: got 12, want 0"
			// so no further formatting is necessary here.
			t.Error(err)
		}
	})
}

Validator functions

The most generic validation function is

check.Validate(query, validationFn func(Value[T]) error)

This Validator validates by running validationFn on the query's value and returning any resulting error.

check also provides a number of shorthands for common cases:

  • check.Equal(query, want) checks that the query's value is want.
  • check.NotEqual(query, wantNot) checks that the query has any value other than wantNot.
  • check.Present(query) checks that the query has any value at all.
  • check.NotPresent(query) checks that the query is unset.
  • check.EqualOrNil(query, want) checks that the query's value is want OR that the query is unset.
  • check.Predicate[T](query PathStruct[T], wantMsg string, predicate func(T) bool) checks that the value at query is present and satisfies the given predicate function; wantMsg is used in the resulting error if it fails.

These helpers all have prewritten validation functions that return sensible errors of the form "<path>: <got>, <want>", such as:

/system/some/path: got no value, want 12
/system/hostname: got "wrongname", want "node1" or nil
/some/other/path: got 100, want no value

Validating a Validator

Given a Validator, there are several ways to test its condition:

  • vd.Check(client) executes the query immediately, tests the result, and returns any error generated by the validation function.
  • vd.Await(ctx, client) will watch the specified path and return nil as soon as the validation passes; if this never happens, it will continue blocking until the context expires or is canceled.
  • vd.AwaitUntil(deadline, client) is almost the same as creating a context with the given deadline and calling Await(), except that if the deadline is in the past it will call Check() instead.
  • vd.AwaitFor(timeout, client) is AwaitUntil(time.Now().Add(timeout), client)

Accommodating latency

There will often be some small latency between when a configuration variable is set via gNMI and when the corresponding operational state reflects the change. As a result, it's common to want to test several values with the expectation that all of them will be correct within some short window of time. The preferred way to do this is:

deadline := time.Now().Add(time.Second())
for _, vd:= range[]check.Validator {
	check.Equal(root.Some().Path(), someValue),
	check.Present(root.Some().OtherPath()),
	...
	check.NotEqual(root.Another().Path(), anotherValue),
} {
	t.Run(vd.Path(), func(t *testing.T) {
		if err := vd.AwaitUntil(deadline, client); err != nil {
			t.Error(err)
		}
	})
}

The above code expects that every validation will pass within one second of the start of the block. Don't use AwaitFor this, or else on a failing device the test could wait one second *per validator* instead of total.

Note that the above is also preferable to

ctx, _ := context.WithTimeout(context.Background(), time.Second())
for _, vd:= range[]check.Validator {
	...
} {
	t.Run(vd.Path(), func(t *testing.T) {
		if err := vd.Await(ctx, client); err != nil {
			t.Error(err)
		}
	})
}

This is because if the first validator times out, the other validators won't run because Await(ctx, client) aborts if the ctx has already expired, whereas AwaitUntil and AwaitFor will both be equivalent to Check if given a 0 or negative timeout or a deadline in the past.

Error Messages

The error messages generated by failing checks will include the path, the value at that path, and a description of what the validator wanted, e.g.

some/path: got 12, want 19

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatPath

func FormatPath(path ygnmi.PathStruct) string

FormatPath formats a PathStruct for display. On unresolvable or otherwise broken path structs, it will return a string indicating that this is an unprintable path.

func FormatRelativePath

func FormatRelativePath(base, path ygnmi.PathStruct) string

FormatRelativePath formats a path relative to base.

func FormatValue

func FormatValue[T any](v *ygnmi.Value[T]) string

FormatValue formats v's value, or returns "no value" if v isn't present.

Types

type Validator

type Validator interface {
	Check(*ygnmi.Client) error
	Await(context.Context, *ygnmi.Client) error
	AwaitFor(time.Duration, *ygnmi.Client) error
	AwaitUntil(time.Time, *ygnmi.Client) error
	Path() string
	RelPath(ygnmi.PathStruct) string
}

Validator is an interface representing a validation operation that could have latency. The Check method fetches a value and validates it immediately; the Await* methods watch the query's value waiting for a value that passes validation. Note that AwaitFor and AwaitUntil are equivalent to Check if you pass in a negative duration or deadline in the past.

func Equal

func Equal[T any, QT ygnmi.SingletonQuery[T]](query QT, want T) Validator

Equal expects the query's value to be want.

func EqualOrNil

func EqualOrNil[T any, QT ygnmi.SingletonQuery[T]](query QT, want T) Validator

EqualOrNil expects the query to be unset or have value want.

func NotEqual

func NotEqual[T any, QT ygnmi.SingletonQuery[T]](query QT, wantNot T) Validator

NotEqual expects the query to have a value other than wantNot.

func NotPresent

func NotPresent[T any, QT ygnmi.SingletonQuery[T]](query QT) Validator

NotPresent expects the query to not have a value set.

func Predicate

func Predicate[T any, QT ygnmi.SingletonQuery[T]](query QT, wantMsg string, predicate func(T) bool) Validator

Predicate expects that the query has a value and the given predicate returns true on that value. The wantMsg will be included in any validation failure, e.g. if wantMsg is "want a multiple of 4", the error might read:

"/some/path: got 13, want a multiple of 4".

func Present

func Present[T any, QT ygnmi.SingletonQuery[T]](query QT) Validator

Present expects the query to have any value.

func UnorderedEqual

func UnorderedEqual[T any, QT ygnmi.SingletonQuery[[]T]](query QT, want []T, less func(a, b T) bool) Validator

UnorderedEqual function is used to compare slices of type T in unordered way.

func Validate

func Validate[T any, QT ygnmi.SingletonQuery[T]](query QT, validationFn func(*ygnmi.Value[T]) error) Validator

Validate expects validationFn to return no error on the query's value.

Jump to

Keyboard shortcuts

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