view testharness/src/bin/testharness.rs @ 163:a75a66cb4181

Add end-to-end tests; fix issues found by tests. - Create tests and installer/remover shell script - Fix Pointer/pointee problems - Add Debug formatting - Misc cleanup
author Paul Fisher <paul@pfish.zone>
date Mon, 14 Jul 2025 17:40:11 -0400
parents a2676475e86b
children 2f5913131295
line wrap: on
line source

//! The actual program which runs the tests.

use nonstick::conv::Exchange;
use nonstick::items::Items;
use nonstick::libpam::TransactionBuilder;
use nonstick::{Conversation, ErrorCode, Flags, LibPamTransaction, PamShared, Transaction};
use std::cell::Cell;
use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;

fn main() {
    test_wrong_user();
    test_wrong_password();
    test_correct();
}

#[derive(Debug, Default)]
struct TestHarness {
    username_requested: Cell<bool>,
    wrong_username: bool,
    wrong_password: bool,
    changing_password: Cell<bool>,
    change_prompt_count: Cell<u8>,
}

impl Conversation for &TestHarness {
    fn communicate(&self, messages: &[Exchange]) {
        if let [only_msg] = messages {
            match only_msg {
                Exchange::Prompt(p) => {
                    if self.username_requested.get() {
                        panic!("username already requested!")
                    }
                    if self.wrong_username {
                        p.set_answer(Ok(OsString::from("not-right")))
                    } else {
                        p.set_answer(Ok(OsString::from("initial")))
                    }
                    self.username_requested.set(true)
                }
                Exchange::MaskedPrompt(p) => {
                    let answer = if self.changing_password.get() {
                        let prompts = self.change_prompt_count.get();
                        self.change_prompt_count.set(prompts + 1);
                        match prompts {
                            0 => "mistake",
                            1 => "mismatch",
                            2 => "acceptable",
                            3 => "acceptable",
                            _ => panic!("unexpected number of prompts!"),
                        }
                    } else if self.wrong_password {
                        "bogus"
                    } else {
                        "valid"
                    };
                    p.set_answer(Ok(OsString::from(answer)));
                }
                Exchange::Error(e) if self.changing_password.get() => e.set_answer(Ok(())),
                other => panic!("Unknown message {other:?}!"),
            }
        } else {
            for msg in messages {
                match msg {
                    Exchange::Info(i) => i.set_answer(Ok(())),
                    Exchange::Error(e) => e.set_answer(Ok(())),
                    Exchange::Prompt(p) => match p.question().as_bytes() {
                        b"How many?" => p.set_answer(Ok(OsString::from("123"))),
                        _ => p.set_answer(Err(ErrorCode::ConversationError)),
                    },
                    Exchange::MaskedPrompt(p) => match p.question().as_bytes() {
                        b"Where?" => p.set_answer(Ok(OsString::from("abc"))),
                        _ => p.set_answer(Err(ErrorCode::ConversationError)),
                    },
                    other => other.set_error(ErrorCode::Abort),
                }
            }
        }
    }
}

impl TestHarness {
    fn start(&self) -> LibPamTransaction<&Self> {
        TransactionBuilder::new_with_service("nonstick-testharness")
            .build(self)
            .expect("expected build success")
    }
}

fn test_wrong_user() {
    let harness = TestHarness {
        wrong_username: true,
        ..Default::default()
    };
    let mut tx = harness.start();
    let auth = tx.authenticate(Flags::empty());
    assert_eq!(auth, Err(ErrorCode::UserUnknown));
}

fn test_wrong_password() {
    let harness = TestHarness {
        wrong_password: true,
        ..Default::default()
    };
    let mut tx = harness.start();
    let auth = tx.authenticate(Flags::empty());
    assert_eq!(auth, Err(ErrorCode::AuthenticationError));
}

fn test_correct() {
    let harness = TestHarness::default();
    let mut tx = harness.start();
    tx.authenticate(Flags::empty()).unwrap();
    assert_eq!(tx.items().user().unwrap().unwrap(), "updated-in-process");
    let result = tx.account_management(Flags::empty());
    assert_eq!(result, Err(ErrorCode::NewAuthTokRequired));
    harness.changing_password.set(true);
    let change = tx.change_authtok(Flags::CHANGE_EXPIRED_AUTHTOK);
    assert_eq!(change, Err(ErrorCode::TryAgain));
    tx.change_authtok(Flags::CHANGE_EXPIRED_AUTHTOK).unwrap();
}