1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
32 
33 #include <memory>
34 
35 #include "mongo/transport/mock_session.h"
36 #include "mongo/transport/mock_ticket.h"
37 #include "mongo/transport/transport_layer.h"
38 #include "mongo/unittest/unittest.h"
39 #include "mongo/util/net/message.h"
40 #include "mongo/util/time_support.h"
41 
42 namespace mongo {
43 
44 class ServiceEntryPoint;
45 struct SSLPeerInfo;
46 
47 /**
48  * Test class. Uses a mock TransportLayer to test that the ServiceEntryPoint
49  * calls the expected methods on the TransportLayer in the expected order,
50  * and with the expected parameters.
51  *
52  * Usage:
53  *
54  * TEST_F(ServiceEntryPointTestSuite, ServiceEntryPointImplTest) {
55  *    // Set up our ServiceEntryPoint
56  *    auto sepFactory = [](TransportLayer* tl){
57  *       return stdx::make_unique<ServiceEntryPointImpl>(tl);
58  *    };
59  *
60  *    setServiceEntryPoint(sepFactory);
61  *
62  *    // Run some tests
63  *    fullLifeCycleTest();
64  * }
65  */
66 class ServiceEntryPointTestSuite : public mongo::unittest::Test {
67 public:
68     // Need a function that takes a TransportLayer* and returns a new
69     // ServiceEntryPoint
70     using ServiceEntryPointFactory =
71         stdx::function<std::unique_ptr<ServiceEntryPoint>(transport::TransportLayer*)>;
72 
73     void setUp() override;
74 
75     void setServiceEntryPoint(ServiceEntryPointFactory factory);
76 
77     // Lifecycle Tests
78     void noLifeCycleTest();
79     void halfLifeCycleTest();
80     void fullLifeCycleTest();
81 
82     // Concurrent Session Tests
83     void interruptingSessionTest();
84 
85     // Stress Tests
86     void burstStressTest(int numSessions = 1000,
87                          int numCycles = 1,
88                          Milliseconds delay = Milliseconds(0));
89     void longSessionStressTest();
90 
91     class SEPTestSession;
92     using SEPTestSessionHandle = std::shared_ptr<SEPTestSession>;
93 
94     /**
95      * This class mocks the TransportLayer and allows us to insert hooks beneath
96      * its methods.
97      */
98     class MockTLHarness : public transport::TransportLayer {
99     public:
100         friend class SEPTestSession;
101 
102         MockTLHarness();
103 
104         transport::Ticket sourceMessage(
105             const transport::SessionHandle& session,
106             Message* message,
107             Date_t expiration = transport::Ticket::kNoExpirationDate) override;
108         transport::Ticket sinkMessage(
109             const transport::SessionHandle& session,
110             const Message& message,
111             Date_t expiration = transport::Ticket::kNoExpirationDate) override;
112         Status wait(transport::Ticket&& ticket) override;
113         void asyncWait(transport::Ticket&& ticket, TicketCallback callback) override;
114 
115         void end(const transport::SessionHandle& session) override;
116         Status setup() override;
117         Status start() override;
118         void shutdown() override;
119 
120         transport::MockTicket* getMockTicket(const transport::Ticket& ticket);
121 
122         // Mocked method hooks
123         stdx::function<transport::Ticket(const transport::SessionHandle&, Message*, Date_t)>
124             _sourceMessage;
125         stdx::function<transport::Ticket(const transport::SessionHandle&, const Message&, Date_t)>
126             _sinkMessage;
127         stdx::function<Status(transport::Ticket)> _wait;
128         stdx::function<void(transport::Ticket, TicketCallback)> _asyncWait;
129         stdx::function<void(const transport::SessionHandle&)> _end;
130         stdx::function<void(SEPTestSession& session)> _destroy_hook;
131         stdx::function<Status(void)> _start = [] { return Status::OK(); };
132         stdx::function<void(void)> _shutdown = [] {};
133 
134         // Pre-set hook methods
135         transport::Ticket _defaultSource(const transport::SessionHandle& s, Message* m, Date_t d);
136         transport::Ticket _defaultSink(const transport::SessionHandle& s, const Message&, Date_t d);
137         transport::Ticket _sinkThenErrorOnWait(const transport::SessionHandle& s,
138                                                const Message& m,
139                                                Date_t d);
140 
141         Status _defaultWait(transport::Ticket ticket);
142         Status _waitError(transport::Ticket ticket);
143         Status _waitOnceThenError(transport::Ticket ticket);
144 
145         // Reset all hooks to their defaults
146         void _resetHooks();
147 
148     private:
149         void _destroy(SEPTestSession& session);
150     };
151 
152     /**
153      * A light wrapper around the mock session class, to handle our destroy logic.
154      */
155     class SEPTestSession : public transport::MockSession {
156         MockTLHarness* _mockTL;
157 
158     public:
create(MockTLHarness * tl)159         static std::shared_ptr<SEPTestSession> create(MockTLHarness* tl) {
160             std::shared_ptr<SEPTestSession> handle(new SEPTestSession(tl));
161             return handle;
162         }
163 
~SEPTestSession()164         ~SEPTestSession() {
165             _mockTL->_destroy(*this);
166         }
167 
168     private:
SEPTestSession(MockTLHarness * tl)169         explicit SEPTestSession(MockTLHarness* tl) : transport::MockSession(tl), _mockTL(tl) {}
170     };
171 
172 private:
173     std::unique_ptr<MockTLHarness> _tl;
174     std::unique_ptr<ServiceEntryPoint> _sep;
175 };
176 
177 }  // namespace mongo
178