Compare commits

..
Author SHA1 Message Date
Adrien Beraud 0cddee9fa3 proxy_server: detach pending push requests before destruction
~DhtProxyServer destroyed requests_ whose ~http::Request notifies DONE;
the handler registered by sendPushNotification then erased from the map
being destroyed, corrupting the heap (double free under ASan) whenever a
push request was still pending, e.g. an unreachable push server. Detach
the state-change callbacks and release the requests before the members
are destroyed.
2026-09-04 14:43:53 -04:00
Adrien Beraud 3aec1a39d8 storage: re-storing identical content is a refresh, not an edition
Storage::store reported edited_values=1 whenever the stored Sp<Value>
differed from the incoming one, even with equal content. A local put()
of an already-stored value was therefore announced to local listeners
as a new value a second time, unbalancing listen caches that count how
many sources hold a value. Only report an edition when the content
actually changed; the expiration is still refreshed.
2026-09-04 14:43:53 -04:00
Adrien Béraud 8a8b4d41ca op_cache, proxy_client: honor GetCallback return and guard push refresh expiry
- SearchCache::get: if gcb returns false, call dcb(false, {}) instead
  of silently proceeding with dcb(true, {}).
- DhtProxyClient push refresh: skip expiration of old values when the
  get() completion reports failure (ok == false), preventing spurious
  expires on transient errors.

In pushNotificationReceived, track the set of value IDs received
in the refresh GET and only expire values whose IDs are not in the
new set.  Previously all old values were expired on refresh,
incorrectly expiring edited values.
2026-09-04 14:43:53 -04:00
Adrien Beraud 3627090257 value_cache: detect editions by increasing seq, like OpValueCache
ValueCache emitted any content change of a known value as an edition
while OpValueCache only accepts a higher seq. A node reporting an older
version made ValueCache replace its copy and re-emit versions that were
then dropped or, with source counting, double counted downstream. Use
the same rule at both levels.
2026-09-04 14:43:53 -04:00
Adrien Béraud 88af795eaa value_cache, op_cache: fix value expiration after edition
ValueCache::addValues no longer emits an expire callback for the
old version when a value is edited; only the add callback for the
new version is emitted.

Add a grace period in expireValues when the cache is synced, using
the value type's expiration as an extra margin before emitting
expirations.

OpValueCache::onValuesAdded: on edit, expire the old value and add
the new one without incrementing refCount.
SearchCache::get: only serve from cache when synced.
2026-09-04 14:43:53 -04:00
Adrien Béraud 2e509193d4 tests: add listen value edit tests for DhtRunner and proxy
Add testListenValueEdit to DhtRunnerTester: verifies that editing
a value triggers an add callback (not an expire+add), and that
the edited value eventually expires.

Add testListenValueEdit and testPushListenValueEdit to
DhtProxyTester: same semantics tested over the long-poll and push
notification proxy paths.

Fix proxy tearDown order to destroy DhtProxyServer before shutting
down the proxy DhtRunner, preventing use-after-free on cancel.
2026-09-04 14:43:53 -04:00
Adrien Béraud 527ecd9d19 tests: add OpCacheTester and ValueCacheTester
Add unit tests for OpValueCache and ValueCache in isolation.
OpCacheTester covers add/edit/expire flows, refcount management,
and value synchronization lifecycle.
ValueCacheTester covers edition handling and value expiration.
2026-09-04 14:41:11 -04:00
10 changed files with 53 additions and 183 deletions

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ if(POLICY CMP0167)
endif()
set (opendht_VERSION_MAJOR 4)
set (opendht_VERSION_MINOR 4.0)
set (opendht_VERSION_MINOR 3.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
+1 -19
View File
@@ -5,7 +5,7 @@ project(
'cython',
license: 'MIT',
license_files: 'LICENSE',
version: '4.4.0',
version: '4.3.0',
meson_version: '>=1.1.0',
default_options: ['cpp_std=c++20', 'warning_level=3'],
)
@@ -292,24 +292,6 @@ if get_option('tests').enabled()
)
test('NetworkEngine', test_networkengine)
test_opcache = executable(
'test_opcache',
'tests/test_opcache.cpp',
'tests/tests_runner.cpp',
include_directories: opendht_inc,
dependencies: [opendht_dep, cppunit, jsoncpp, fmt, openssl, msgpack],
)
test('OpCache', test_opcache)
test_valuecache = executable(
'test_valuecache',
'tests/test_valuecache.cpp',
'tests/tests_runner.cpp',
include_directories: opendht_inc,
dependencies: [opendht_dep, cppunit, jsoncpp, fmt, openssl, msgpack],
)
test('ValueCache', test_valuecache)
test_crypto = executable(
'test_crypto',
'tests/test_crypto.cpp',
+2 -4
View File
@@ -39,10 +39,8 @@ OpValueCache::onValuesExpired(const std::vector<Sp<Value>>& vals, const system_c
if (vit != values.end()) {
if (vit->second.updated > t)
continue;
// Mirror onValuesAdded: a version that was never counted (different
// content, not a newer seq) must not decrement the refcount
if (*v != *vit->second.data and v->seq <= vit->second.data->seq)
continue;
if (v->seq < vit->second.data->seq)
continue; // source held an older version, not counted
vit->second.updated = t;
vit->second.refCount--;
+1 -1
View File
@@ -189,7 +189,7 @@ private:
v->second.created = now;
v->second.expiration = now + te;
v->second.typeExpiration = te;
if (*value != *v->second.data and value->seq > v->second.data->seq) {
if (value->seq > v->second.data->seq) {
// edited value (same rule as OpValueCache): emit as add
v->second.data = value;
nvals.emplace_back(value);
+47 -61
View File
@@ -16,21 +16,6 @@ using namespace std::chrono_literals;
namespace test {
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
#ifdef OPENDHT_PUSH_NOTIFICATIONS
namespace {
// The result is produced on the DHT thread, which may be blocked in a listen
// callback waiting for the same mutex: never hold it while waiting.
dht::PushNotificationResult
sendPush(dht::DhtRunner& node, const std::map<std::string, std::string>& data, std::unique_lock<std::mutex>& lk)
{
lk.unlock();
auto ret = node.pushNotificationReceived(data).get();
lk.lock();
return ret;
}
} // namespace
#endif
void
DhtProxyTester::setUp()
{
@@ -347,7 +332,7 @@ DhtProxyTester::testPushListenValueEdit()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for first add callback
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
@@ -372,7 +357,7 @@ DhtProxyTester::testPushListenValueEdit()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for the second add
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
@@ -404,7 +389,7 @@ DhtProxyTester::testPushListenValueEdit()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
// Wait for the expire callback
CPPUNIT_ASSERT_MESSAGE("Should receive expire callback via push", cv.wait_for(lk, 10s, [&] {
@@ -556,7 +541,7 @@ DhtProxyTester::testPushNotification()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::Values);
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::Values);
// Wait for client to receive the value
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return gotValues; }));
@@ -585,7 +570,7 @@ DhtProxyTester::testPushNotification()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::Values);
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::Values);
// Wait for client to receive the edited value
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return gotValues; }));
@@ -602,7 +587,7 @@ DhtProxyTester::testPushNotification()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
cv.wait_for(lk, 500ms, [&] { return gotExpiredValues; });
@@ -1133,8 +1118,7 @@ DhtProxyTester::testPushMultiValueEditExpire()
auto key = dht::InfoHash::get("pushMultiEditExpire");
auto identity = dht::crypto::generateIdentity("MultiEditAuthor");
struct Event
{
struct Event {
dht::Value::Id id;
uint16_t seq;
bool expired;
@@ -1192,7 +1176,7 @@ DhtProxyTester::testPushMultiValueEditExpire()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for both values to arrive
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
@@ -1225,7 +1209,7 @@ DhtProxyTester::testPushMultiValueEditExpire()
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
push_data.erase("exp");
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for the edited value (id=100, seq=2) to arrive
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
@@ -1248,11 +1232,13 @@ DhtProxyTester::testPushMultiValueEditExpire()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
CPPUNIT_ASSERT(sendPush(nodeClient, push_data, lk) == dht::PushNotificationResult::ValuesExpired);
CPPUNIT_ASSERT(nodeClient.pushNotificationReceived(push_data).get() == dht::PushNotificationResult::ValuesExpired);
// Wait for expire callback for value B
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
return std::any_of(events.begin(), events.end(), [](const Event& e) { return e.id == 200 && e.expired; });
return std::any_of(events.begin(), events.end(), [](const Event& e) {
return e.id == 200 && e.expired;
});
}));
// Verify: value A (edited) should NOT have been expired
@@ -1302,8 +1288,7 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
auto key = dht::InfoHash::get("pushRefreshNoExpire");
auto identity = dht::crypto::generateIdentity("RefreshAuthor");
struct Event
{
struct Event {
dht::Value::Id id;
bool expired;
};
@@ -1343,7 +1328,7 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for all 3 values to arrive
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
@@ -1369,7 +1354,7 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
push_data.erase("exp");
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait a bit for any callbacks
cv.wait_for(lk, 1s);
@@ -1390,11 +1375,13 @@ DhtProxyTester::testPushRefreshNoSpuriousExpire()
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
nodeClient.pushNotificationReceived(push_data).get();
// Wait for expire callback
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] {
return std::any_of(events.begin(), events.end(), [](const Event& e) { return e.id == 20 && e.expired; });
return std::any_of(events.begin(), events.end(), [](const Event& e) {
return e.id == 20 && e.expired;
});
}));
// Verify only value 20 expired, not 10 or 30
@@ -1439,12 +1426,7 @@ DhtProxyTester::testProxyListenEditChain()
auto key = dht::InfoHash::get("proxyEditChain");
auto identity = dht::crypto::generateIdentity("ProxyChainEditTester");
struct Event
{
dht::Value::Id id;
uint16_t seq;
bool expired;
};
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
std::vector<Event> events;
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
@@ -1511,7 +1493,8 @@ DhtProxyTester::testProxyListenEditChain()
for (const auto& e : events)
if (!e.expired && e.seq > maxSeq)
maxSeq = e.seq;
CPPUNIT_ASSERT_EQUAL_MESSAGE("Latest add should be the final version", (uint16_t) NUM_EDITS, maxSeq);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Latest add should be the final version",
(uint16_t) NUM_EDITS, maxSeq);
}
// Wait for natural expiration
@@ -1535,7 +1518,8 @@ DhtProxyTester::testProxyListenEditChain()
}
}
CPPUNIT_ASSERT_EQUAL_MESSAGE("Should have exactly 1 expire via proxy", 1, expireCount);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Expired value should be the latest", (uint16_t) NUM_EDITS, expiredSeq);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Expired value should be the latest",
(uint16_t) NUM_EDITS, expiredSeq);
}
nodeClient.cancelListen(key, ftoken.get());
@@ -1573,12 +1557,7 @@ DhtProxyTester::testPushRapidEditsNoPhantom()
auto key = dht::InfoHash::get("pushRapidEdits");
auto identity = dht::crypto::generateIdentity("RapidEditAuthor");
struct Event
{
dht::Value::Id id;
uint16_t seq;
bool expired;
};
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
std::vector<Event> events;
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
@@ -1614,9 +1593,12 @@ DhtProxyTester::testPushRapidEditsNoPhantom()
std::map<std::string, std::string> push_data;
push_data["key"] = key.toString();
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
lk.unlock();
nodeClient.pushNotificationReceived(push_data).get();
lk.lock();
}
// Wait for edits to arrive (at least 2: initial + latest)
@@ -1687,12 +1669,7 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
auto key = dht::InfoHash::get("pushRefreshAfterEdit");
auto identity = dht::crypto::generateIdentity("RefreshEditAuthor");
struct Event
{
dht::Value::Id id;
uint16_t seq;
bool expired;
};
struct Event { dht::Value::Id id; uint16_t seq; bool expired; };
std::vector<Event> events;
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
@@ -1728,9 +1705,12 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
std::map<std::string, std::string> push_data;
push_data["key"] = key.toString();
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
lk.unlock();
nodeClient.pushNotificationReceived(push_data).get();
lk.lock();
}
// Wait for initial add
@@ -1762,9 +1742,12 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
std::map<std::string, std::string> push_data;
push_data["key"] = key.toString();
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
lk.unlock();
nodeClient.pushNotificationReceived(push_data).get();
lk.lock();
}
// Wait for the edit add (seq=3)
@@ -1782,9 +1765,12 @@ DhtProxyTester::testPushRefreshAfterEditKeepsLatest()
std::map<std::string, std::string> push_data;
push_data["key"] = key.toString();
push_data["t"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
sendPush(nodeClient, push_data, lk);
lk.unlock();
nodeClient.pushNotificationReceived(push_data).get();
lk.lock();
}
// Wait a bit for any callbacks
-49
View File
@@ -210,32 +210,6 @@ OpCacheTester::testStaleExpireIgnored()
CPPUNIT_ASSERT_EQUAL((uint16_t) 2, events.back().seq);
}
void
OpCacheTester::testSameSeqConflictExpireIgnored()
{
// A different payload with the same seq (e.g. the same signed content
// re-signed with a randomized signature) is ignored on add and not
// counted: its expiration must be ignored too.
std::vector<Event> events;
dht::OpValueCache cache(recordEvents(events));
auto x = makeValue(60, 0, "x");
auto y = makeValue(60, 0, "y");
cache.onValuesAdded({x}); // A
cache.onValuesAdded({y}); // B: same seq, different content
CPPUNIT_ASSERT_EQUAL(1u, count(events, false));
CPPUNIT_ASSERT(cache.get(60)->data == x->data);
cache.onValuesExpired({y}); // B
CPPUNIT_ASSERT_EQUAL_MESSAGE("An uncounted version must not expire the value", (size_t) 1, cache.size());
CPPUNIT_ASSERT_EQUAL(0u, count(events, true));
cache.onValuesExpired({x}); // A
CPPUNIT_ASSERT_EQUAL((size_t) 0, cache.size());
CPPUNIT_ASSERT_EQUAL(1u, count(events, true));
}
void
OpCacheTester::testCallbacks()
{
@@ -569,29 +543,6 @@ OpCacheTester::testSourceDropKeepsValue()
CPPUNIT_ASSERT_EQUAL((size_t) 0, searchCache.get(dht::Value::Filter {}).size());
}
void
OpCacheTester::testSeqOnlyRefreshNotCounted()
{
// A node re-announcing the same content with a bumped seq must not be
// counted as an additional source by the OpValueCache above it, or the
// value would survive the node's departure.
std::vector<Event> events;
dht::OpValueCache parent(recordEvents(events));
dht::TypeStore types;
auto now = dht::clock::now();
auto v = makeValue(42, 0, "same");
auto vBumped = makeValue(42, 1, "same");
{
dht::ValueCache node([&](const Values& vals, bool expired) { parent.onValue(vals, expired); });
node.onValues({v}, {}, {}, types, now);
node.onValues({vBumped}, {}, {}, types, now);
CPPUNIT_ASSERT_EQUAL(1u, count(events, false));
}
CPPUNIT_ASSERT_EQUAL_MESSAGE("Node gone: its value must be expired once", 1u, count(events, true));
CPPUNIT_ASSERT_EQUAL((size_t) 0, parent.size());
}
void
OpCacheTester::testHighChurnRefCountConsistency()
{
-4
View File
@@ -16,7 +16,6 @@ class OpCacheTester : public CppUnit::TestFixture
CPPUNIT_TEST(testEditChainMultiSource);
CPPUNIT_TEST(testStaleAddIgnored);
CPPUNIT_TEST(testStaleExpireIgnored);
CPPUNIT_TEST(testSameSeqConflictExpireIgnored);
CPPUNIT_TEST(testCallbacks);
CPPUNIT_TEST(testFilters);
CPPUNIT_TEST(testSyncStatus);
@@ -27,7 +26,6 @@ class OpCacheTester : public CppUnit::TestFixture
CPPUNIT_TEST(testGetCallbackReturnsFalse);
CPPUNIT_TEST(testTimestampOrdering);
CPPUNIT_TEST(testSourceDropKeepsValue);
CPPUNIT_TEST(testSeqOnlyRefreshNotCounted);
CPPUNIT_TEST(testHighChurnRefCountConsistency);
CPPUNIT_TEST(testValueUpdateSingleSourcePhantom);
CPPUNIT_TEST(testExpireAfterUpdate);
@@ -45,7 +43,6 @@ public:
void testEditChainMultiSource();
void testStaleAddIgnored();
void testStaleExpireIgnored();
void testSameSeqConflictExpireIgnored();
void testCallbacks();
void testFilters();
void testSyncStatus();
@@ -56,7 +53,6 @@ public:
void testGetCallbackReturnsFalse();
void testTimestampOrdering();
void testSourceDropKeepsValue();
void testSeqOnlyRefreshNotCounted();
void testHighChurnRefCountConsistency();
void testValueUpdateSingleSourcePhantom();
void testExpireAfterUpdate();
-41
View File
@@ -353,47 +353,6 @@ ValueCacheTester::testSameSeqEditIgnored()
CPPUNIT_ASSERT_EQUAL(2, addCount);
}
void
ValueCacheTester::testSameContentHigherSeqIsRefresh()
{
// Value::operator== ignores seq: identical content with a bumped seq is
// the same value re-announced, not an edition. Re-emitting it would make
// a source-counting OpValueCache count this node twice.
int addCount = 0;
dht::ValueCache cache([&](const std::vector<std::shared_ptr<dht::Value>>& vals, bool expired) {
if (!expired)
addCount += vals.size();
});
dht::TypeStore types;
auto now = std::chrono::steady_clock::now();
auto v1 = std::make_shared<dht::Value>();
v1->id = 1;
v1->seq = 0;
v1->data = {'A'};
cache.onValues({v1}, {}, {}, types, now);
CPPUNIT_ASSERT_EQUAL(1, addCount);
auto sameContent = std::make_shared<dht::Value>();
sameContent->id = 1;
sameContent->seq = 1;
sameContent->data = {'A'};
auto later = now + std::chrono::minutes(5);
cache.onValues({sameContent}, {}, {}, types, later);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Same content with a higher seq is a refresh, not an edit", 1, addCount);
// The refresh still extends the expiration (default type: 10 minutes)
cache.expireValues(now + std::chrono::minutes(11));
CPPUNIT_ASSERT_EQUAL((size_t) 1, cache.size());
auto edited = std::make_shared<dht::Value>();
edited->id = 1;
edited->seq = 2;
edited->data = {'B'};
cache.onValues({edited}, {}, {}, types, later);
CPPUNIT_ASSERT_EQUAL(2, addCount);
}
void
ValueCacheTester::testShortExpirationRefreshLost()
{
-2
View File
@@ -20,7 +20,6 @@ class ValueCacheTester : public CppUnit::TestFixture
CPPUNIT_TEST(testMaxValues);
CPPUNIT_TEST(testUpdateTypeExpiration);
CPPUNIT_TEST(testSameSeqEditIgnored);
CPPUNIT_TEST(testSameContentHigherSeqIsRefresh);
CPPUNIT_TEST(testShortExpirationRefreshLost);
CPPUNIT_TEST(testShortExpirationHighChurn);
CPPUNIT_TEST(testGracePeriodWhileSynced);
@@ -41,7 +40,6 @@ public:
void testMaxValues();
void testUpdateTypeExpiration();
void testSameSeqEditIgnored();
void testSameContentHigherSeqIsRefresh();
void testShortExpirationRefreshLost();
void testShortExpirationHighChurn();
void testGracePeriodWhileSynced();
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "opendht",
"version-string": "4.4.0",
"version-string": "4.3.0",
"port-version": 1,
"description": "OpenDHT: A lightweight C++17 Distributed Hash Table library.",
"homepage": "https://opendht.net",