KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_http_lib_connection.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
22#include <chrono>
23#include <thread>
24
27
29
30BOOST_AUTO_TEST_SUITE( HttpLibConnection )
31
32
33static const std::string RootUrl = "http://fake.test/v1/";
34
35
37{
38 HTTP_LIB_SOURCE source;
40 source.root_url = RootUrl;
41 return source;
42}
43
44
46{
48 aServer.InstallStandardLibrary( source.root_url );
49
50 return HTTP_LIB_CONNECTION( source, true, aServer.MakeRetriever() );
51}
52
53
54BOOST_AUTO_TEST_CASE( ValidEndpointSyncsCategories )
55{
58
59 BOOST_CHECK( conn.IsValidEndpoint() );
60
61 BOOST_REQUIRE_EQUAL( conn.getCategories().size(), 2u );
62 BOOST_CHECK_EQUAL( conn.getCategories()[0].name, "Resistors" );
63 BOOST_CHECK_EQUAL( conn.getCategoryDescription( "Resistors" ), "Resistors" );
64 BOOST_CHECK_EQUAL( conn.getCategoryDescription( "Capacitors" ), "Capacitors" );
65}
66
67
68BOOST_AUTO_TEST_CASE( EmptyResponseIsInvalid )
69{
71 server.SetResponse( RootUrl, { 200, "" } );
72
73 HTTP_LIB_CONNECTION conn( makeTestSource(), true, server.MakeRetriever() );
74
75 BOOST_CHECK( !conn.IsValidEndpoint() );
76 BOOST_CHECK( conn.GetLastError().find( "empty response" ) != std::string::npos );
77}
78
79
80BOOST_AUTO_TEST_CASE( Non200ResponseIsInvalid )
81{
83 server.SetResponse( RootUrl, { 500, "" } );
84
85 HTTP_LIB_CONNECTION conn( makeTestSource(), true, server.MakeRetriever() );
86
87 BOOST_CHECK( !conn.IsValidEndpoint() );
88 BOOST_CHECK( conn.GetLastError().find( "error code" ) != std::string::npos );
89}
90
91
92BOOST_AUTO_TEST_CASE( MalformedJsonIsInvalid )
93{
95 server.SetResponse( RootUrl, { 200, "not json" } );
96
97 HTTP_LIB_CONNECTION conn( makeTestSource(), true, server.MakeRetriever() );
98
99 BOOST_CHECK( !conn.IsValidEndpoint() );
100 BOOST_CHECK( !conn.GetLastError().empty() );
101}
102
103
104BOOST_AUTO_TEST_CASE( TransportFailureIsInvalid )
105{
107 makeTestSource(), true,
108 []( const std::string&, int& aStatusCode, std::string&, std::string& aError )
109 {
110 aStatusCode = 0;
111 aError = "connection refused";
112 return false;
113 } );
114
115 BOOST_CHECK( !conn.IsValidEndpoint() );
116 BOOST_CHECK_EQUAL( conn.GetLastError(), "connection refused" );
117}
118
119
120BOOST_AUTO_TEST_CASE( SelectAllPopulatesPartsAndCache )
121{
124
125 HTTP_LIB_CATEGORY category;
126 category.id = "1";
127
128 std::vector<HTTP_LIB_PART> parts;
129
130 BOOST_CHECK( conn.SelectAll( category, parts ) );
131 BOOST_REQUIRE_EQUAL( parts.size(), 2u );
132 BOOST_CHECK_EQUAL( parts[0].name, "R_10K" );
133 BOOST_CHECK( !parts[0].detailsLoaded );
134
135 std::string partId;
136 std::string categoryId;
137 BOOST_CHECK( conn.GetCachedPartRelation( "R_10K", partId, categoryId ) );
138 BOOST_CHECK_EQUAL( partId, "res-10k" );
139 BOOST_CHECK_EQUAL( categoryId, "1" );
140}
141
142
143BOOST_AUTO_TEST_CASE( SelectOneFetchesAndCaches )
144{
147
148 HTTP_LIB_PART part;
149
150 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
151 BOOST_CHECK_EQUAL( part.name, "R_10K" );
152 BOOST_CHECK( part.detailsLoaded );
153
154 bool foundFootprint = false;
155
156 for( const auto& [name, properties] : part.fields )
157 {
158 if( name == "footprint" )
159 {
160 foundFootprint = true;
161 BOOST_CHECK_EQUAL( std::get<0>( properties ), "Resistor_SMD:R_0603_1608Metric" );
162 }
163 }
164
165 BOOST_CHECK( foundFootprint );
166
167 // The second lookup must be served from the part cache without another request.
168 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
169 BOOST_CHECK_EQUAL( server.RequestCount( RootUrl + "parts/res-10k.json" ), 1 );
170}
171
172
173BOOST_AUTO_TEST_CASE( SelectOneRefetchesWhenCacheExpired )
174{
177
179 source.timeout_parts = 0;
180
181 HTTP_LIB_CONNECTION conn( source, true, server.MakeRetriever() );
182
183 HTTP_LIB_PART part;
184
185 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
186 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
187
188 BOOST_CHECK_EQUAL( server.RequestCount( RootUrl + "parts/res-10k.json" ), 2 );
189}
190
191
192BOOST_AUTO_TEST_CASE( SelectOneReports404 )
193{
196 server.SetResponse( RootUrl + "parts/missing.json", { 404, "" } );
197
198 HTTP_LIB_CONNECTION conn( makeTestSource(), true, server.MakeRetriever() );
199
200 HTTP_LIB_PART part;
201 BOOST_CHECK( !conn.SelectOne( "missing", part ) );
202 BOOST_CHECK( conn.GetLastError().find( "404" ) != std::string::npos );
203}
204
205
206BOOST_AUTO_TEST_CASE( SelectAllRejectsNonJsonBody )
207{
210 server.SetResponse( RootUrl + "parts/category/9.json", { 200, "not json" } );
211
212 HTTP_LIB_CONNECTION conn( makeTestSource(), true, server.MakeRetriever() );
213
214 HTTP_LIB_CATEGORY category;
215 category.id = "9";
216
217 std::vector<HTTP_LIB_PART> parts;
218 BOOST_CHECK( !conn.SelectAll( category, parts ) );
219}
220
221
222BOOST_AUTO_TEST_CASE( SelectOneFetchCacheLifetime )
223{
226
228 source.timeout_parts = 1;
229
230 HTTP_LIB_CONNECTION conn( source, true, server.MakeRetriever() );
231
232 HTTP_LIB_PART part;
233
234 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
235 BOOST_CHECK_EQUAL( server.RequestCount( RootUrl + "parts/res-10k.json" ), 1 );
236
237 // An immediate repeat must be served from cache.
238 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
239 BOOST_CHECK_EQUAL( server.RequestCount( RootUrl + "parts/res-10k.json" ), 1 );
240
241 // Once the cache lifetime has elapsed the record must be re-fetched.
242 std::this_thread::sleep_for( std::chrono::milliseconds( 1100 ) );
243
244 BOOST_CHECK( conn.SelectOne( "res-10k", part ) );
245 BOOST_CHECK_EQUAL( server.RequestCount( RootUrl + "parts/res-10k.json" ), 2 );
246}
247
248
250
const char * name
bool GetCachedPartRelation(const std::string &aPartName, std::string &aPartId, std::string &aCategoryId) const
Resolve a cached part name to its part ID and category ID.
std::vector< HTTP_LIB_CATEGORY > getCategories() const
bool SelectAll(const HTTP_LIB_CATEGORY &aCategory, std::vector< HTTP_LIB_PART > &aParts)
Retrieve all parts from a specific category from the HTTP library.
bool SelectOne(const std::string &aPartID, HTTP_LIB_PART &aFetchedPart)
Retrieve a single part with full details from the HTTP library.
std::string GetLastError() const
std::string getCategoryDescription(const std::string &aCategoryName) const
In-process HTTP library server used by QA tests.
int RequestCount(const std::string &aUrl) const
Number of times aUrl has been requested.
void InstallStandardLibrary(const std::string &aRootUrl)
void SetResponse(const std::string &aUrl, RESPONSE aResponse)
Replace the response served for an exact URL.
HTTP_LIB_CONNECTION::RETRIEVER MakeRetriever()
STL namespace.
std::string id
id of category
std::vector< std::pair< std::string, field_type > > fields
Connection parameters for one HTTP library.
HTTP_LIB_SOURCE_TYPE type
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
static HTTP_LIB_CONNECTION connectToStandardLibrary(HTTP_LIB_FAKE_SERVER &aServer)
BOOST_AUTO_TEST_CASE(ValidEndpointSyncsCategories)
static HTTP_LIB_SOURCE makeTestSource()
static const std::string RootUrl
BOOST_CHECK_EQUAL(result, "25.4")