The outbound ports, as far as they can be held to a contract without calling anything: what they do when they have nothing to call with. (What a real vendor says back is the adapter's own test.)

4use ::log::info;
5use whiskers_core::{Age, Direction};
6use whiskers_ports::{Clock, Judge, JudgeError, SecretName, SpeakError, SpeechLine, Voice, run_ready};

A judge with no credentials never says Allow and never pretends to have decided.

9pub fn judge_without_credentials<J: Judge>(j: &J) {
10    info!("conformance: judge without credentials");
11    let age = Age::new(6).unwrap();
12    for direction in [Direction::FromChild, Direction::ToChild] {
13        assert_eq!(run_ready(j.check(direction, age, "hello")), Err(JudgeError::NotConfigured(SecretName::JevKey)), "no key is not an allow");
14    }
15    let shortlist = ["a".to_owned(), "b".to_owned()];
16    assert_eq!(run_ready(j.rerank("hello", &shortlist)), Err(JudgeError::NotConfigured(SecretName::JevKey)));
17}

A voice with no credentials says so and does not try.

20pub fn voice_without_credentials<V: Voice>(v: &V) {
21    info!("conformance: voice without credentials");
22    assert!(!run_ready(v.configured()));
23    let line = SpeechLine::new("hello there".into()).unwrap();
24    assert_eq!(run_ready(v.speak(&line)), Err(SpeakError::NotConfigured));
25    assert_eq!(run_ready(v.speak_timed(&line)), Err(SpeakError::NotConfigured));
26}

A clock reads a plausible time and does not run backwards.

29pub fn clock<C: Clock>(c: &C) {
30    let (a, b) = (c.now(), c.now());
31    assert!(a.get() > 1_600_000_000_000, "milliseconds since the epoch, after 2020: {}", a.get());
32    assert!(b >= a, "time does not run backwards");
33}