@@ -553,9 +553,13 @@ class MyMockDomain : public mqbmock::Domain {
553553 mqbmock::Dispatcher* d_mockDispatcher;
554554 bmqp_ctrlmsg::RoutingConfiguration d_routingConfiguration;
555555 bsl::shared_ptr<MyMockQueueHandle> d_queueHandle;
556- MyQueueEngine d_mockQueueEngine;
557- const bool d_atMostOnce;
558- bslma::Allocator* d_allocator_p;
556+ // Previously created handles, kept alive so that their addresses remain
557+ // distinct across successive `openQueue` calls (tests relying on handle
558+ // pointer identity, e.g. duplicate-queueId, depend on this).
559+ bsl::vector<bsl::shared_ptr<MyMockQueueHandle> > d_retiredHandles;
560+ MyQueueEngine d_mockQueueEngine;
561+ const bool d_atMostOnce;
562+ bslma::Allocator* d_allocator_p;
559563
560564 // CREATORS
561565
@@ -567,6 +571,7 @@ class MyMockDomain : public mqbmock::Domain {
567571 : mqbmock::Domain(cluster, allocator)
568572 , d_mockDispatcher(dispatcher)
569573 , d_queueHandle()
574+ , d_retiredHandles(allocator)
570575 , d_mockQueueEngine(allocator)
571576 , d_atMostOnce(atMostOnce)
572577 , d_allocator_p(allocator)
@@ -595,6 +600,12 @@ class MyMockDomain : public mqbmock::Domain {
595600 queue->_setAtMostOnce (d_atMostOnce);
596601 queue->_setDispatcher (d_mockDispatcher);
597602
603+ // Retain the previously created handle so its address is not reused by
604+ // the handle created below.
605+ if (d_queueHandle) {
606+ d_retiredHandles.push_back (d_queueHandle);
607+ }
608+
598609 d_queueHandle.createInplace (d_allocator_p,
599610 queue,
600611 clientContext,
@@ -2262,6 +2273,79 @@ static void test11_initiateShutdown()
22622273 }
22632274}
22642275
2276+ static void test12_openQueueDuplicateQueueId ()
2277+ // ------------------------------------------------------------------------
2278+ // TESTS OPEN QUEUE WITH A DUPLICATE QUEUE ID
2279+ //
2280+ // Concerns:
2281+ // - A misbehaving client may send a second openQueue request reusing a
2282+ // queueId that is already associated with a different queue. The
2283+ // broker must reject that request gracefully (returning an error to
2284+ // the client).
2285+ //
2286+ // Plan:
2287+ // Instantiate a testbench, open a first queue with a given queueId, then
2288+ // send a second openQueue for a *different* URI reusing the same
2289+ // queueId. Verify the first response is a successful OpenQueueResponse
2290+ // and the second is an E_INVALID_ARGUMENT Status.
2291+ //
2292+ // Testing:
2293+ // That reusing a queueId for a different queue is rejected.
2294+ // ------------------------------------------------------------------------
2295+ {
2296+ bmqtst::TestHelper::printTestName (
2297+ " TESTS OPEN QUEUE WITH A DUPLICATE QUEUE ID" );
2298+
2299+ const bsl::string uri1 (" bmq://my.domain/queue-first" ,
2300+ bmqtst::TestHelperUtil::allocator ());
2301+ const bsl::string uri2 (" bmq://my.domain/queue-second" ,
2302+ bmqtst::TestHelperUtil::allocator ());
2303+ const int queueId = 1 ;
2304+
2305+ TestBench tb (client (e_FirstHop),
2306+ false , // atMostOnce
2307+ bmqtst::TestHelperUtil::allocator ());
2308+
2309+ // Open the first queue: expect a successful OpenQueueResponse.
2310+ tb.openQueue (uri1, queueId);
2311+ tb.d_cs .flush ();
2312+
2313+ {
2314+ bmqio::TestChannel::WriteCall writeCall;
2315+ BMQTST_ASSERT (tb.d_channel ->getWriteCall (&writeCall, 0 ));
2316+
2317+ bmqp::Event event (&writeCall.d_blob ,
2318+ bmqtst::TestHelperUtil::allocator ());
2319+ BMQTST_ASSERT (event.isControlEvent ());
2320+
2321+ bmqp_ctrlmsg::ControlMessage response (
2322+ bmqtst::TestHelperUtil::allocator ());
2323+ BMQTST_ASSERT_EQ (0 , event.loadControlEvent (&response));
2324+ BMQTST_ASSERT (response.choice ().isOpenQueueResponseValue ());
2325+ }
2326+
2327+ // Open a *different* queue reusing the same queueId: expect a failure
2328+ // Status rather than a crash or a silent overwrite.
2329+ tb.openQueue (uri2, queueId);
2330+ tb.d_cs .flush ();
2331+
2332+ {
2333+ bmqio::TestChannel::WriteCall writeCall;
2334+ BMQTST_ASSERT (tb.d_channel ->getWriteCall (&writeCall, 1 ));
2335+
2336+ bmqp::Event event (&writeCall.d_blob ,
2337+ bmqtst::TestHelperUtil::allocator ());
2338+ BMQTST_ASSERT (event.isControlEvent ());
2339+
2340+ bmqp_ctrlmsg::ControlMessage response (
2341+ bmqtst::TestHelperUtil::allocator ());
2342+ BMQTST_ASSERT_EQ (0 , event.loadControlEvent (&response));
2343+ BMQTST_ASSERT (response.choice ().isStatusValue ());
2344+ BMQTST_ASSERT_EQ (response.choice ().status ().category (),
2345+ bmqp_ctrlmsg::StatusCategory::E_INVALID_ARGUMENT );
2346+ }
2347+ }
2348+
22652349static void testN1_ackConfiguration ()
22662350// ------------------------------------------------------------------------
22672351// TESTS ACK CONFIGURATION FOR CLIENT SESSION
@@ -2512,6 +2596,7 @@ int main(int argc, char* argv[])
25122596
25132597 switch (_testCase) {
25142598 case 0 :
2599+ case 12 : test12_openQueueDuplicateQueueId (); break ;
25152600 case 11 : test11_initiateShutdown (); break ;
25162601 case 10 : test10_newStyleCompressedPush (); break ;
25172602 case 9 : test9_newStylePush (); break ;
0 commit comments