atagen

malice

public
Star0
Watch0
6d7dff75a67df5f7dc8a1843de20e5bc4db5769e
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: crates/malice-push/tests/send.rs

aa3dce5114d0atagen2026-10-26 06:09 UTC1
use aws_lc_rs::agreement::{ECDH_P256, PrivateKey};
aa3dce5114d0atagen2026-10-26 06:09 UTC2
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
aa3dce5114d0atagen2026-10-26 06:09 UTC3
use malice_push::{Allowlist, Message, Outcome, Sender, Subscription, Urgency, Vapid, ece};
aa3dce5114d0atagen2026-10-26 06:09 UTC4
use wiremock::{
aa3dce5114d0atagen2026-10-26 06:09 UTC5
    Mock, MockServer, ResponseTemplate,
aa3dce5114d0atagen2026-10-26 06:09 UTC6
    matchers::{header, method, path},
aa3dce5114d0atagen2026-10-26 06:09 UTC7
};
aa3dce5114d0atagen2026-10-26 06:09 UTC8
aa3dce5114d0atagen2026-10-26 06:09 UTC9
struct Browser {
aa3dce5114d0atagen2026-10-26 06:09 UTC10
    private: PrivateKey,
aa3dce5114d0atagen2026-10-26 06:09 UTC11
    auth: [u8; 16],
aa3dce5114d0atagen2026-10-26 06:09 UTC12
}
aa3dce5114d0atagen2026-10-26 06:09 UTC13
aa3dce5114d0atagen2026-10-26 06:09 UTC14
impl Browser {
aa3dce5114d0atagen2026-10-26 06:09 UTC15
    fn new() -> Self {
aa3dce5114d0atagen2026-10-26 06:09 UTC16
        Self {
aa3dce5114d0atagen2026-10-26 06:09 UTC17
            private: PrivateKey::generate(&ECDH_P256).unwrap(),
aa3dce5114d0atagen2026-10-26 06:09 UTC18
            auth: [3; 16],
aa3dce5114d0atagen2026-10-26 06:09 UTC19
        }
aa3dce5114d0atagen2026-10-26 06:09 UTC20
    }
aa3dce5114d0atagen2026-10-26 06:09 UTC21
aa3dce5114d0atagen2026-10-26 06:09 UTC22
    fn subscription(&self, endpoint: String) -> Subscription {
aa3dce5114d0atagen2026-10-26 06:09 UTC23
        Subscription {
aa3dce5114d0atagen2026-10-26 06:09 UTC24
            endpoint,
aa3dce5114d0atagen2026-10-26 06:09 UTC25
            p256dh: URL_SAFE_NO_PAD.encode(self.private.compute_public_key().unwrap().as_ref()),
aa3dce5114d0atagen2026-10-26 06:09 UTC26
            auth: URL_SAFE_NO_PAD.encode(self.auth),
aa3dce5114d0atagen2026-10-26 06:09 UTC27
        }
aa3dce5114d0atagen2026-10-26 06:09 UTC28
    }
aa3dce5114d0atagen2026-10-26 06:09 UTC29
}
aa3dce5114d0atagen2026-10-26 06:09 UTC30
aa3dce5114d0atagen2026-10-26 06:09 UTC31
async fn sender(server: &MockServer, dir: &tempfile::TempDir) -> Sender {
aa3dce5114d0atagen2026-10-26 06:09 UTC32
    let authority = server.address().to_string();
aa3dce5114d0atagen2026-10-26 06:09 UTC33
    Sender::new(
aa3dce5114d0atagen2026-10-26 06:09 UTC34
        Vapid::load_or_create(&dir.path().join("vapid.pk8")).unwrap(),
aa3dce5114d0atagen2026-10-26 06:09 UTC35
        "mailto:test@example.org".to_owned(),
aa3dce5114d0atagen2026-10-26 06:09 UTC36
        Allowlist::with_extra([authority]),
aa3dce5114d0atagen2026-10-26 06:09 UTC37
    )
aa3dce5114d0atagen2026-10-26 06:09 UTC38
    .unwrap()
aa3dce5114d0atagen2026-10-26 06:09 UTC39
}
aa3dce5114d0atagen2026-10-26 06:09 UTC40
aa3dce5114d0atagen2026-10-26 06:09 UTC41
#[tokio::test]
aa3dce5114d0atagen2026-10-26 06:09 UTC42
async fn delivers_encrypted_and_reports_gone_retry_and_refusal() {
aa3dce5114d0atagen2026-10-26 06:09 UTC43
    let server = MockServer::start().await;
aa3dce5114d0atagen2026-10-26 06:09 UTC44
    Mock::given(method("POST"))
aa3dce5114d0atagen2026-10-26 06:09 UTC45
        .and(path("/push/ok"))
aa3dce5114d0atagen2026-10-26 06:09 UTC46
        .and(header("content-encoding", "aes128gcm"))
aa3dce5114d0atagen2026-10-26 06:09 UTC47
        .and(header("urgency", "high"))
aa3dce5114d0atagen2026-10-26 06:09 UTC48
        .and(header("ttl", "43200"))
aa3dce5114d0atagen2026-10-26 06:09 UTC49
        .respond_with(ResponseTemplate::new(201))
aa3dce5114d0atagen2026-10-26 06:09 UTC50
        .expect(1)
aa3dce5114d0atagen2026-10-26 06:09 UTC51
        .mount(&server)
aa3dce5114d0atagen2026-10-26 06:09 UTC52
        .await;
aa3dce5114d0atagen2026-10-26 06:09 UTC53
    Mock::given(method("POST"))
aa3dce5114d0atagen2026-10-26 06:09 UTC54
        .and(path("/push/gone"))
aa3dce5114d0atagen2026-10-26 06:09 UTC55
        .respond_with(ResponseTemplate::new(410))
aa3dce5114d0atagen2026-10-26 06:09 UTC56
        .mount(&server)
aa3dce5114d0atagen2026-10-26 06:09 UTC57
        .await;
aa3dce5114d0atagen2026-10-26 06:09 UTC58
    Mock::given(method("POST"))
aa3dce5114d0atagen2026-10-26 06:09 UTC59
        .and(path("/push/flaky"))
aa3dce5114d0atagen2026-10-26 06:09 UTC60
        .respond_with(ResponseTemplate::new(503))
aa3dce5114d0atagen2026-10-26 06:09 UTC61
        .up_to_n_times(1)
aa3dce5114d0atagen2026-10-26 06:09 UTC62
        .with_priority(1)
aa3dce5114d0atagen2026-10-26 06:09 UTC63
        .mount(&server)
aa3dce5114d0atagen2026-10-26 06:09 UTC64
        .await;
aa3dce5114d0atagen2026-10-26 06:09 UTC65
    Mock::given(method("POST"))
aa3dce5114d0atagen2026-10-26 06:09 UTC66
        .and(path("/push/flaky"))
aa3dce5114d0atagen2026-10-26 06:09 UTC67
        .respond_with(ResponseTemplate::new(201))
aa3dce5114d0atagen2026-10-26 06:09 UTC68
        .with_priority(2)
aa3dce5114d0atagen2026-10-26 06:09 UTC69
        .mount(&server)
aa3dce5114d0atagen2026-10-26 06:09 UTC70
        .await;
aa3dce5114d0atagen2026-10-26 06:09 UTC71
aa3dce5114d0atagen2026-10-26 06:09 UTC72
    let dir = tempfile::tempdir().unwrap();
aa3dce5114d0atagen2026-10-26 06:09 UTC73
    let sender = sender(&server, &dir).await;
aa3dce5114d0atagen2026-10-26 06:09 UTC74
    let browser = Browser::new();
aa3dce5114d0atagen2026-10-26 06:09 UTC75
    let message = Message {
aa3dce5114d0atagen2026-10-26 06:09 UTC76
        payload: br#"{"web_push":8030}"#,
aa3dce5114d0atagen2026-10-26 06:09 UTC77
        urgency: Urgency::High,
aa3dce5114d0atagen2026-10-26 06:09 UTC78
        topic: "!room:example.org",
aa3dce5114d0atagen2026-10-26 06:09 UTC79
    };
aa3dce5114d0atagen2026-10-26 06:09 UTC80
aa3dce5114d0atagen2026-10-26 06:09 UTC81
    let ok = browser.subscription(format!("{}/push/ok", server.uri()));
aa3dce5114d0atagen2026-10-26 06:09 UTC82
    assert_eq!(sender.send(&ok, &message).await, Outcome::Delivered);
aa3dce5114d0atagen2026-10-26 06:09 UTC83
    let requests = server.received_requests().await.unwrap();
aa3dce5114d0atagen2026-10-26 06:09 UTC84
    let request = &requests[0];
aa3dce5114d0atagen2026-10-26 06:09 UTC85
    let authorization = request.headers["authorization"].to_str().unwrap();
aa3dce5114d0atagen2026-10-26 06:09 UTC86
    assert!(authorization.starts_with("vapid t="));
aa3dce5114d0atagen2026-10-26 06:09 UTC87
    assert!(authorization.ends_with(&format!("k={}", sender.vapid_public_key())));
aa3dce5114d0atagen2026-10-26 06:09 UTC88
    assert_eq!(request.headers["topic"].to_str().unwrap().len(), 32);
aa3dce5114d0atagen2026-10-26 06:09 UTC89
    let plain = ece::decrypt(&browser.private, &browser.auth, &request.body).unwrap();
aa3dce5114d0atagen2026-10-26 06:09 UTC90
    assert_eq!(plain, message.payload);
aa3dce5114d0atagen2026-10-26 06:09 UTC91
aa3dce5114d0atagen2026-10-26 06:09 UTC92
    let gone = browser.subscription(format!("{}/push/gone", server.uri()));
aa3dce5114d0atagen2026-10-26 06:09 UTC93
    let outcome = sender.send(&gone, &message).await;
aa3dce5114d0atagen2026-10-26 06:09 UTC94
    assert_eq!(outcome, Outcome::Gone);
aa3dce5114d0atagen2026-10-26 06:09 UTC95
    assert!(outcome.drops_subscription());
aa3dce5114d0atagen2026-10-26 06:09 UTC96
aa3dce5114d0atagen2026-10-26 06:09 UTC97
    let flaky = browser.subscription(format!("{}/push/flaky", server.uri()));
aa3dce5114d0atagen2026-10-26 06:09 UTC98
    assert_eq!(sender.send(&flaky, &message).await, Outcome::Delivered);
aa3dce5114d0atagen2026-10-26 06:09 UTC99
aa3dce5114d0atagen2026-10-26 06:09 UTC100
    let elsewhere = browser.subscription("https://example.org/push".to_owned());
aa3dce5114d0atagen2026-10-26 06:09 UTC101
    assert_eq!(sender.send(&elsewhere, &message).await, Outcome::Refused);
aa3dce5114d0atagen2026-10-26 06:09 UTC102
    let bad_keys = Subscription {
aa3dce5114d0atagen2026-10-26 06:09 UTC103
        p256dh: "AAAA".to_owned(),
aa3dce5114d0atagen2026-10-26 06:09 UTC104
        ..ok
aa3dce5114d0atagen2026-10-26 06:09 UTC105
    };
aa3dce5114d0atagen2026-10-26 06:09 UTC106
    assert_eq!(sender.send(&bad_keys, &message).await, Outcome::Refused);
aa3dce5114d0atagen2026-10-26 06:09 UTC107
}