geopro/tests/net/test_auth_loads.cpp

154 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// AuthLoads 离线单测CaptchaLoad/LoginLoad 句柄行为done/failed/abort 闸门),
// 用 FakeApiCall + 真 ApiChain 离线驱动不联网。QSignalSpy 需 Qt6::Test。
#include <gtest/gtest.h>
#include <stdexcept>
#include <QSignalSpy>
#include "ApiChain.hpp"
#include "ApiClient.hpp"
#include "AuthLoads.hpp"
#include "AuthService.hpp"
#include "net/FakeApiCall.hpp"
using namespace geopro::net;
using geopro::net::test::FakeApiCall;
namespace {
ApiResponse captchaOk() {
ApiResponse r;
r.code = 200;
r.httpStatus = 200;
r.data = QJsonObject{{"id", "cid-1"}, {"code", "AB12"}};
return r;
}
ApiResponse tokenOk() {
ApiResponse r;
r.code = 200;
r.httpStatus = 200;
r.data = QJsonObject{{"accessToken", "Geomative deadbeef"}};
return r;
}
ApiResponse plainOk() {
ApiResponse r;
r.code = 200;
r.httpStatus = 200;
return r;
}
ApiResponse failResp() {
ApiResponse r;
r.code = 500;
r.httpStatus = 200;
r.msg = QStringLiteral("验证码错误");
return r;
}
auto isFailure = [](const ApiResponse& r) { return r.code != 200 || !r.rawError.isEmpty(); };
} // namespace
TEST(CaptchaLoad, ParsesCaptchaOnSuccess) {
auto* call = new FakeApiCall;
auto* load = new CaptchaLoad(call);
QSignalSpy doneSpy(load, &CaptchaLoad::done);
QSignalSpy failSpy(load, &CaptchaLoad::failed);
call->fire(captchaOk());
ASSERT_EQ(doneSpy.count(), 1);
EXPECT_EQ(failSpy.count(), 0);
auto cap = doneSpy.takeFirst().at(0).value<AuthService::Captcha>();
EXPECT_EQ(cap.codeId, QStringLiteral("cid-1"));
EXPECT_EQ(cap.code, QStringLiteral("AB12"));
}
TEST(CaptchaLoad, EmitsFailedOnErrorResponse) {
auto* call = new FakeApiCall;
auto* load = new CaptchaLoad(call);
QSignalSpy doneSpy(load, &CaptchaLoad::done);
QSignalSpy failSpy(load, &CaptchaLoad::failed);
call->fire(failResp());
EXPECT_EQ(doneSpy.count(), 0);
ASSERT_EQ(failSpy.count(), 1);
EXPECT_EQ(failSpy.takeFirst().at(0).toString(), QStringLiteral("验证码错误"));
}
TEST(CaptchaLoad, AbortGateSuppressesLateSignal) {
auto* call = new FakeApiCall;
auto* load = new CaptchaLoad(call);
QSignalSpy doneSpy(load, &CaptchaLoad::done);
load->abort();
EXPECT_TRUE(call->aborted);
call->fire(captchaOk()); // 迟到
EXPECT_EQ(doneSpy.count(), 0);
}
TEST(LoginLoad, EmitsTokenOnChainSuccess) {
auto* s1 = new FakeApiCall;
auto* s2 = new FakeApiCall;
QList<ApiChain::StepFactory> steps{
[&](const QList<ApiResponse>&) -> IApiCall* { return s1; },
[&](const QList<ApiResponse>&) -> IApiCall* { return s2; }};
auto* chain = new ApiChain(steps, isFailure);
auto* load = new LoginLoad(chain);
QSignalSpy doneSpy(load, &LoginLoad::done);
QSignalSpy failSpy(load, &LoginLoad::failed);
s1->fire(plainOk()); // verifyCodeCheck 通过 → 触发 step2
s2->fire(tokenOk()); // login2 返回 token
ASSERT_EQ(doneSpy.count(), 1);
EXPECT_EQ(failSpy.count(), 0);
EXPECT_EQ(doneSpy.takeFirst().at(0).toString(), QStringLiteral("Geomative deadbeef"));
}
TEST(LoginLoad, EmitsFailedWhenChainFails) {
auto* s1 = new FakeApiCall;
QList<ApiChain::StepFactory> steps{
[&](const QList<ApiResponse>&) -> IApiCall* { return s1; }};
auto* chain = new ApiChain(steps, isFailure);
auto* load = new LoginLoad(chain);
QSignalSpy doneSpy(load, &LoginLoad::done);
QSignalSpy failSpy(load, &LoginLoad::failed);
s1->fire(failResp());
EXPECT_EQ(doneSpy.count(), 0);
ASSERT_EQ(failSpy.count(), 1);
EXPECT_EQ(failSpy.takeFirst().at(0).toString(), QStringLiteral("验证码错误"));
}
TEST(LoginLoad, EmitsFailedWhenTokenMissing) {
auto* s1 = new FakeApiCall;
QList<ApiChain::StepFactory> steps{
[&](const QList<ApiResponse>&) -> IApiCall* { return s1; }};
auto* chain = new ApiChain(steps, isFailure);
auto* load = new LoginLoad(chain);
QSignalSpy doneSpy(load, &LoginLoad::done);
QSignalSpy failSpy(load, &LoginLoad::failed);
s1->fire(plainOk()); // 成功但无 accessToken
EXPECT_EQ(doneSpy.count(), 0);
ASSERT_EQ(failSpy.count(), 1);
}
// M-2step 工厂抛异常(模拟 RSA 失败)时 LoginLoad 应 emit failed。
// 第一步用 FakeApiCall不同步 fire由测试手动触发第二步工厂抛异常。
// 构造顺序new ApiChain同步执行 step1 工厂s1 已建立但未 fire
// → new LoginLoad连接 chain 的 succeeded/failed
// → s1->fire(plainOk())step1 成功 → step2 工厂抛 → ApiChain emit failed
// → LoginLoad 已连接,收到 failed
TEST(LoginLoad, EmitsFailedWhenStepFactoryThrows) {
auto* s1 = new FakeApiCall;
QList<ApiChain::StepFactory> steps{
[&](const QList<ApiResponse>&) -> IApiCall* { return s1; },
[&](const QList<ApiResponse>&) -> IApiCall* {
throw std::runtime_error("rsa fail");
}};
auto* chain = new ApiChain(steps, isFailure);
auto* load = new LoginLoad(chain);
QSignalSpy doneSpy(load, &LoginLoad::done);
QSignalSpy failSpy(load, &LoginLoad::failed);
s1->fire(plainOk()); // step1 成功 → step2 工厂抛异常 → ApiChain emit failed
ASSERT_EQ(failSpy.count(), 1);
EXPECT_EQ(doneSpy.count(), 0);
}