harden(net): ApiBatch 契约断言(非空calls/非空谓词)+fail-fast注释+单元素测试(评审 I-1/I-2/M-1/M-2)

This commit is contained in:
gaozheng 2026-06-11 20:13:48 +08:00
parent 72b300d722
commit e980ddd346
2 changed files with 11 additions and 1 deletions

View File

@ -4,6 +4,8 @@ namespace geopro::net {
ApiBatch::ApiBatch(QList<IApiCall*> calls, Predicate isFailure, QObject* parent) ApiBatch::ApiBatch(QList<IApiCall*> calls, Predicate isFailure, QObject* parent)
: QObject(parent), isFailure_(std::move(isFailure)) { : QObject(parent), isFailure_(std::move(isFailure)) {
Q_ASSERT(!calls.isEmpty()); // 契约:至少一个 call空 batch 永不发 succeeded调用方会挂起
Q_ASSERT(isFailure_); // 契约:必须提供失败谓词(否则首个 finished 即 bad_function_call
responses_.resize(calls.size()); responses_.resize(calls.size());
remaining_ = static_cast<int>(calls.size()); remaining_ = static_cast<int>(calls.size());
for (int i = 0; i < calls.size(); ++i) { for (int i = 0; i < calls.size(); ++i) {
@ -12,7 +14,7 @@ ApiBatch::ApiBatch(QList<IApiCall*> calls, Predicate isFailure, QObject* parent)
QObject::connect(c, &IApiCall::finished, this, [this, i](const ApiResponse& resp) { QObject::connect(c, &IApiCall::finished, this, [this, i](const ApiResponse& resp) {
if (aborted_) return; // §5.0 入口守卫 if (aborted_) return; // §5.0 入口守卫
if (isFailure_(resp)) { if (isFailure_(resp)) {
aborted_ = true; aborted_ = true; // 此后 remaining_ 不再维护:迟到 finished 全被入口守卫挡掉
for (const auto& other : calls_) { // fail-fastabort 其余在飞 for (const auto& other : calls_) { // fail-fastabort 其余在飞
if (other) other->abort(); if (other) other->abort();
} }

View File

@ -26,6 +26,14 @@ TEST(ApiBatch, SucceedsWhenAllOk) {
EXPECT_EQ(failSpy.count(), 0); EXPECT_EQ(failSpy.count(), 0);
} }
TEST(ApiBatch, SucceedsWithSingleCall) {
auto* a = new FakeApiCall;
auto* batch = new ApiBatch({a}, isFailure);
QSignalSpy okSpy(batch, &ApiBatch::succeeded);
a->fire(ok()); // N=1一次 fire 即触发 --remaining_==0
EXPECT_EQ(okSpy.count(), 1);
}
TEST(ApiBatch, FailFastAbortsOthers) { TEST(ApiBatch, FailFastAbortsOthers) {
auto* a = new FakeApiCall; auto* a = new FakeApiCall;
auto* b = new FakeApiCall; // 慢的(永不 fire auto* b = new FakeApiCall; // 慢的(永不 fire