Files
Jungfraujoch/tests/RugnuxLiveStatusTest.cpp
leonarski_fandClaude Opus 5.5 02e5bc387f broker: initialize and start clear the last collection's live rugnux status
/rugnux/status kept answering with the previous collection's rugnux run after POST /initialize,
and also after a /start whose collection does not run rugnux - the latter under the new run's
tokens. RugnuxLiveStatus gets a Clear(), called from JFJochReceiverService::ClearResults(), which
/start already uses to drop the previous run's status, plots and buffered images before the tokens
change. Initialize now calls ClearResults() instead of ClearImageBuffer(), so a fresh start also
drops the previous run's receiver status and plots.

Authentication: every rugnux endpoint (GET/PUT /config/rugnux, PUT/DELETE /config/rugnux/model,
GET /rugnux/status) was already in the broker's bearer-protected set and carries bearerAuth in
jfjoch_api.yaml; the HTTP auth test now checks all five.

The yaml descriptions of /initialize and rugnux_live_status say so; the C++ model and frontend
client are regenerated (comments only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4
2026-10-20 01:00:54 +02:00

89 lines
3.0 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <catch2/catch_all.hpp>
#include "../receiver/RugnuxLiveStatus.h"
#include "../image_puller/TestImagePuller.h"
using namespace std::chrono_literals;
TEST_CASE("RugnuxLiveStatus_TimesFromFirstImage", "[RugnuxLive]") {
// 11 images at 100 Hz, the first 3 s after START, reported out of order as several forward threads do
const auto start = std::chrono::steady_clock::now() - 10s;
const auto first = start + 3s;
RugnuxLiveStatus status;
status.Start(11, 0.11, start);
for (int i: {1, 0, 2, 3, 4, 5, 6, 7, 8, 10, 9})
status.ImageReceived(first + i * 10ms);
auto s = status.Get();
CHECK(s.expected_collection_s.value() == Catch::Approx(0.11));
REQUIRE(s.images_received == 11);
REQUIRE(s.start_to_first_image_s.value() == Catch::Approx(3.0));
CHECK(!s.last_image_s);
// "now" is 10 s after START, 7 s after the first image
CHECK(s.elapsed_s == Catch::Approx(7.0).margin(0.5));
REQUIRE(status.LastImage() == first + 100ms);
s = status.Get();
REQUIRE(s.last_image_s.value() == Catch::Approx(0.1));
CHECK((s.images_received - 1) / s.last_image_s.value() == Catch::Approx(100.0));
}
TEST_CASE("RugnuxLiveStatus_BeforeFirstImage", "[RugnuxLive]") {
RugnuxLiveStatus status;
status.Start(10, std::nullopt, std::chrono::steady_clock::now() - 2s);
status.Step("waiting");
const auto s = status.Get();
CHECK(!s.start_to_first_image_s);
CHECK(!s.expected_collection_s);
CHECK(s.elapsed_s == 0.0);
REQUIRE(s.steps.size() == 1);
CHECK(s.steps[0].started_s <= 0.0);
CHECK(!status.LastImage());
}
TEST_CASE("RugnuxLiveStatus_Clear", "[RugnuxLive]") {
RugnuxLiveStatus status;
status.Start(10, 1.0, std::chrono::steady_clock::now() - 2s);
status.ImageReceived(std::chrono::steady_clock::now() - 1s);
status.Step("indexing");
status.Probe("lattice found");
status.Result("OK", "fine", {{"Space group", "P 1"}}, {"a warning"});
status.Finish(RugnuxLiveState::Done);
REQUIRE(status.LastImage());
status.Clear();
const auto s = status.Get();
CHECK(s.state == RugnuxLiveState::Idle);
CHECK(s.images_expected == 0);
CHECK(s.images_received == 0);
CHECK(s.steps.empty());
CHECK(s.probe.empty());
CHECK(s.verdict.empty());
CHECK(s.summary.empty());
CHECK(s.warnings.empty());
CHECK(!s.start_to_first_image_s);
CHECK(!s.last_image_s);
CHECK(s.elapsed_s == 0.0);
CHECK(!status.LastImage());
}
TEST_CASE("TestImagePuller_SetsReceiveTime", "[RugnuxLive]") {
TestImagePuller puller;
const auto before = std::chrono::steady_clock::now();
puller.Put(ImagePullerOutput{});
const auto given = before - 5s;
puller.Put(ImagePullerOutput{.received = given});
auto out = puller.PollImage();
REQUIRE(out);
CHECK(out->received >= before);
out = puller.PollImage();
REQUIRE(out);
CHECK(out->received == given);
}