Skip to content

Commit 1f7252c

Browse files
FiveTechSoftclaude
andcommitted
test(network): M12.14/15 wire-bridge end-to-end test
Adds a single 5-subcase test that round-trips every M12.14 and M12.15 ABI bridge over a real tcp:// AdsConnect60 + AdsOpenTable session against an in-process Server: * field metadata: GetNumFields, GetFieldName(1), GetFieldType, GetFieldLength, GetFieldDecimals * cursor state: AtBOF, GetRecordNum, IsRecordDeleted, GotoBottom * info: GetTableType, GetRecordLength, GetNumIndexes * lock + maintenance: LockTable, UnlockTable, LockRecord, UnlockRecord, RefreshRecord, FlushFileBuffers * AOF over wire: SetAOF("TAG='BBBB'"), GetAOFOptLevel, walk confirms only recno=2 is visible, ClearAOF restores walk 368 / 368 tests, +67 assertions; verifies all 17 newly-bridged entry points actually round-trip correctly via the wire layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 97beaa2 commit 1f7252c

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

tests/unit/network_server_test.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,124 @@ TEST_CASE("M12.5 dual-mode AdsConnect60 with tcp:// URI routes ABI calls to serv
334334
fs::remove_all(dir, ec);
335335
}
336336

337+
TEST_CASE("M12.14/15 remote field metadata + cursor + info + AOF round-trip") {
338+
namespace fs = std::filesystem;
339+
auto dir = fs::temp_directory_path() / "openads_m12_14_15";
340+
std::error_code ec;
341+
fs::remove_all(dir, ec);
342+
fs::create_directories(dir);
343+
m12_write_dbf(dir / "data.dbf", {"AAAA", "BBBB", "CCCC"});
344+
345+
Server srv;
346+
REQUIRE(srv.start("127.0.0.1", 0).has_value());
347+
348+
char uri[256];
349+
std::snprintf(uri, sizeof(uri),
350+
"tcp://127.0.0.1:%u/%s",
351+
static_cast<unsigned>(srv.port()),
352+
dir.string().c_str());
353+
UNSIGNED8 srvbuf[256];
354+
std::memcpy(srvbuf, uri, std::strlen(uri) + 1);
355+
UNSIGNED8 leaf[64] = "data.dbf";
356+
357+
ADSHANDLE hConn = 0;
358+
REQUIRE(AdsConnect60(srvbuf, ADS_REMOTE_SERVER,
359+
nullptr, nullptr, 0, &hConn) == 0);
360+
ADSHANDLE hT = 0;
361+
REQUIRE(AdsOpenTable(hConn, leaf, nullptr, ADS_CDX,
362+
0, 0, 0, 0, &hT) == 0);
363+
364+
SUBCASE("M12.14 field metadata bridges (no per-field round-trip)") {
365+
UNSIGNED16 nf = 0;
366+
REQUIRE(AdsGetNumFields(hT, &nf) == 0);
367+
CHECK(nf == 1);
368+
369+
UNSIGNED8 nm[32] = {0};
370+
UNSIGNED16 nlen = sizeof(nm);
371+
REQUIRE(AdsGetFieldName(hT, 1, nm, &nlen) == 0);
372+
std::string fname((char*)nm, nlen);
373+
CHECK(fname == "TAG");
374+
375+
UNSIGNED16 ftype = 0;
376+
REQUIRE(AdsGetFieldType(hT, (UNSIGNED8*)"TAG", &ftype) == 0);
377+
CHECK(ftype == ADS_STRING);
378+
379+
UNSIGNED32 flen = 0;
380+
REQUIRE(AdsGetFieldLength(hT, (UNSIGNED8*)"TAG", &flen) == 0);
381+
CHECK(flen == 4);
382+
383+
UNSIGNED16 fdec = 99;
384+
REQUIRE(AdsGetFieldDecimals(hT, (UNSIGNED8*)"TAG", &fdec) == 0);
385+
CHECK(fdec == 0);
386+
}
387+
388+
SUBCASE("M12.14 cursor state bridges") {
389+
REQUIRE(AdsGotoTop(hT) == 0);
390+
391+
UNSIGNED16 atbof = 99;
392+
REQUIRE(AdsAtBOF(hT, &atbof) == 0);
393+
CHECK(atbof == 0);
394+
395+
UNSIGNED32 rn = 0;
396+
REQUIRE(AdsGetRecordNum(hT, 0, &rn) == 0);
397+
CHECK(rn == 1);
398+
399+
UNSIGNED16 del = 99;
400+
REQUIRE(AdsIsRecordDeleted(hT, &del) == 0);
401+
CHECK(del == 0);
402+
403+
REQUIRE(AdsGotoBottom(hT) == 0);
404+
REQUIRE(AdsGetRecordNum(hT, 0, &rn) == 0);
405+
CHECK(rn == 3);
406+
}
407+
408+
SUBCASE("M12.15 info bridges") {
409+
UNSIGNED16 ttype = 0;
410+
REQUIRE(AdsGetTableType(hT, &ttype) == 0);
411+
CHECK(ttype == ADS_CDX);
412+
413+
UNSIGNED32 rl = 0;
414+
REQUIRE(AdsGetRecordLength(hT, &rl) == 0);
415+
CHECK(rl == 5); // 1 (delete byte) + 4 (TAG)
416+
417+
UNSIGNED16 ni = 99;
418+
REQUIRE(AdsGetNumIndexes(hT, &ni) == 0);
419+
CHECK(ni == 0);
420+
}
421+
422+
SUBCASE("M12.15 lock + maintenance bridges (no-op success)") {
423+
REQUIRE(AdsLockTable(hT) == 0);
424+
REQUIRE(AdsUnlockTable(hT) == 0);
425+
REQUIRE(AdsLockRecord(hT, 1) == 0);
426+
REQUIRE(AdsUnlockRecord(hT, 1) == 0);
427+
REQUIRE(AdsRefreshRecord(hT) == 0);
428+
REQUIRE(AdsFlushFileBuffers(hT) == 0);
429+
}
430+
431+
SUBCASE("M12.15 AOF over the wire") {
432+
UNSIGNED8 cond[32] = "TAG = 'BBBB'";
433+
REQUIRE(AdsSetAOF(hT, cond, 0) == 0);
434+
UNSIGNED16 lvl = 99;
435+
REQUIRE(AdsGetAOFOptLevel(hT, &lvl, nullptr, nullptr) == 0);
436+
CHECK((lvl == ADS_OPTIMIZED_NONE ||
437+
lvl == ADS_OPTIMIZED_FULL ||
438+
lvl == ADS_OPTIMIZED_PART));
439+
REQUIRE(AdsGotoTop(hT) == 0);
440+
UNSIGNED32 rn = 0;
441+
REQUIRE(AdsGetRecordNum(hT, 0, &rn) == 0);
442+
CHECK(rn == 2); // BBBB row only, AAAA / CCCC filtered out
443+
REQUIRE(AdsClearAOF(hT) == 0);
444+
REQUIRE(AdsGotoTop(hT) == 0);
445+
REQUIRE(AdsGetRecordNum(hT, 0, &rn) == 0);
446+
CHECK(rn == 1);
447+
}
448+
449+
REQUIRE(AdsCloseTable(hT) == 0);
450+
REQUIRE(AdsDisconnect(hConn) == 0);
451+
srv.stop();
452+
fs::remove_all(dir, ec);
453+
}
454+
337455
TEST_CASE("M12.6 remote append + set_field + delete + recall + flush round-trip") {
338456
namespace fs = std::filesystem;
339457
auto dir = fs::temp_directory_path() / "openads_m12_6";

0 commit comments

Comments
 (0)