Skip to content

Commit ee28d35

Browse files
committed
test(trogon-github): cover ack failure and ack timeout handler paths
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent 1151eac commit ee28d35

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

  • rsworkspace/crates/trogon-github/src

rsworkspace/crates/trogon-github/src/server.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,125 @@ mod tests {
458458
assert_eq!(resp.status(), StatusCode::OK);
459459
assert_eq!(publisher.published_payloads(), vec![Bytes::new()]);
460460
}
461+
462+
// ── ack failure / timeout paths ──────────────────────────────────────────
463+
464+
mod ack_test_support {
465+
use super::*;
466+
use async_nats::jetstream::publish::PublishAck;
467+
use std::sync::Arc;
468+
use std::sync::Mutex;
469+
use trogon_nats::mocks::MockError;
470+
471+
#[derive(Clone)]
472+
enum AckBehavior {
473+
Fail,
474+
Hang,
475+
}
476+
477+
#[derive(Clone)]
478+
pub struct AckFailPublisher {
479+
behavior: Arc<Mutex<AckBehavior>>,
480+
}
481+
482+
impl AckFailPublisher {
483+
pub fn failing() -> Self {
484+
Self {
485+
behavior: Arc::new(Mutex::new(AckBehavior::Fail)),
486+
}
487+
}
488+
489+
pub fn hanging() -> Self {
490+
Self {
491+
behavior: Arc::new(Mutex::new(AckBehavior::Hang)),
492+
}
493+
}
494+
}
495+
496+
pub enum AckFuture {
497+
Fail,
498+
Hang,
499+
}
500+
501+
impl IntoFuture for AckFuture {
502+
type Output = Result<PublishAck, MockError>;
503+
type IntoFuture = std::pin::Pin<Box<dyn Future<Output = Self::Output> + Send>>;
504+
505+
fn into_future(self) -> Self::IntoFuture {
506+
match self {
507+
AckFuture::Fail => {
508+
Box::pin(async { Err(MockError("simulated ack failure".to_string())) })
509+
}
510+
AckFuture::Hang => Box::pin(std::future::pending()),
511+
}
512+
}
513+
}
514+
515+
impl JetStreamPublisher for AckFailPublisher {
516+
type PublishError = MockError;
517+
type AckFuture = AckFuture;
518+
519+
async fn publish_with_headers<S: ToSubject + Send>(
520+
&self,
521+
_subject: S,
522+
_headers: async_nats::HeaderMap,
523+
_payload: Bytes,
524+
) -> Result<AckFuture, MockError> {
525+
let behavior = self.behavior.lock().unwrap().clone();
526+
match behavior {
527+
AckBehavior::Fail => Ok(AckFuture::Fail),
528+
AckBehavior::Hang => Ok(AckFuture::Hang),
529+
}
530+
}
531+
}
532+
}
533+
534+
use ack_test_support::AckFailPublisher;
535+
use async_nats::subject::ToSubject;
536+
537+
#[tokio::test]
538+
async fn ack_failure_returns_500() {
539+
let publisher = AckFailPublisher::failing();
540+
541+
let state = AppState {
542+
js: publisher,
543+
webhook_secret: None,
544+
subject_prefix: "github".to_string(),
545+
nats_ack_timeout: Duration::from_secs(10),
546+
};
547+
548+
let app = Router::new()
549+
.route("/webhook", post(handle_webhook::<AckFailPublisher>))
550+
.with_state(state);
551+
552+
let resp = app
553+
.oneshot(webhook_request(b"{}", "push", "del-9", None))
554+
.await
555+
.unwrap();
556+
557+
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
558+
}
559+
560+
#[tokio::test]
561+
async fn ack_timeout_returns_500() {
562+
let publisher = AckFailPublisher::hanging();
563+
564+
let state = AppState {
565+
js: publisher,
566+
webhook_secret: None,
567+
subject_prefix: "github".to_string(),
568+
nats_ack_timeout: Duration::from_millis(10),
569+
};
570+
571+
let app = Router::new()
572+
.route("/webhook", post(handle_webhook::<AckFailPublisher>))
573+
.with_state(state);
574+
575+
let resp = app
576+
.oneshot(webhook_request(b"{}", "push", "del-10", None))
577+
.await
578+
.unwrap();
579+
580+
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
581+
}
461582
}

0 commit comments

Comments
 (0)