20#include <boost/test/unit_test.hpp>
28#include <wx/filename.h>
33wxFileName schemaPath()
35 wxFileName schemaFile = wxFileName::DirName( wxString::FromUTF8( QA_SRC_ROOT ) );
36 schemaFile.AppendDir( wxS(
"resources" ) );
37 schemaFile.AppendDir( wxS(
"schemas" ) );
38 schemaFile.SetFullName( wxS(
"kicad-remote-provider-metadata-v1.schema.json" ) );
43nlohmann::json providerMetadataJson(
const wxString& aAuthType = wxS(
"oauth2" ) )
45 nlohmann::json auth = { {
"type", aAuthType.ToStdString() } };
47 if( aAuthType == wxS(
"oauth2" ) )
49 auth[
"metadata_url"] =
"https://provider.example.test/.well-known/oauth-authorization-server";
50 auth[
"client_id"] =
"kicad-desktop";
51 auth[
"scopes"] = nlohmann::json::array( {
"openid",
"parts.read" } );
54 return nlohmann::json{
55 {
"provider_name",
"Acme Parts" },
56 {
"provider_version",
"1.0.0" },
57 {
"api_base_url",
"https://provider.example.test/api" },
58 {
"panel_url",
"https://provider.example.test/app" },
59 {
"session_bootstrap_url",
"https://provider.example.test/session/bootstrap" },
62 { {
"web_ui_v1",
true },
64 {
"direct_downloads_v1",
true },
65 {
"inline_payloads_v1",
true } } },
66 {
"max_download_bytes", 10485760 },
67 {
"supported_asset_types", nlohmann::json::array( {
"symbol",
"footprint",
"3dmodel" } ) },
68 {
"parts", { {
"endpoint_template",
"/v1/parts/{part_id}" } } }
76 std::optional<REMOTE_PROVIDER_METADATA> metadata =
79 if( !metadata.has_value() )
80 throw std::runtime_error( error.ToStdString() );
86nlohmann::json authServerMetadataJson()
88 return nlohmann::json{ {
"issuer",
"https://provider.example.test" },
89 {
"authorization_endpoint",
"https://provider.example.test/oauth/authorize" },
90 {
"token_endpoint",
"https://provider.example.test/oauth/token" },
91 {
"revocation_endpoint",
"https://provider.example.test/oauth/revoke" } };
94nlohmann::json manifestJson()
96 return nlohmann::json{
97 {
"part_id",
"acme-res-10k" },
98 {
"display_name",
"10k Resistor" },
99 {
"summary",
"10k 0603 thick film resistor" },
100 {
"license",
"CC-BY-4.0" },
101 {
"assets", nlohmann::json::array(
102 { { {
"asset_type",
"symbol" },
103 {
"name",
"acme-res-10k.kicad_sym" },
104 {
"content_type",
"application/x-kicad-symbol" },
105 {
"size_bytes", 2048 },
106 {
"sha256",
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" },
107 {
"download_url",
"https://provider.example.test/downloads/acme-res-10k.kicad_sym" },
108 {
"required",
true } },
109 { {
"asset_type",
"footprint" },
110 {
"name",
"R_0603.pretty" },
111 {
"content_type",
"application/x-kicad-footprint" },
112 {
"size_bytes", 4096 },
113 {
"sha256",
"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" },
114 {
"download_url",
"https://provider.example.test/downloads/R_0603.pretty" },
115 {
"required",
false } } } ) }
120wxString dumpJson(
const nlohmann::json& aJson )
122 return wxString::FromUTF8( aJson.dump().c_str() );
124struct BUILD_DIR_FIXTURE
128 m_runFromBuildDirWasSet = wxGetEnv( wxS(
"KICAD_RUN_FROM_BUILD_DIR" ), &m_oldRunFromBuildDir );
130#if defined( __WXMAC__ ) || defined( __WXMSW__ )
132 wxUnsetEnv( wxS(
"KICAD_RUN_FROM_BUILD_DIR" ) );
134 wxFileName resourcesDir = wxFileName::DirName( wxString::FromUTF8( QA_SRC_ROOT ) );
135 resourcesDir.AppendDir( wxS(
"resources" ) );
136 m_stockDataHomeWasSet = wxGetEnv( wxS(
"KICAD_STOCK_DATA_HOME" ), &m_oldStockDataHome );
137 wxSetEnv( wxS(
"KICAD_STOCK_DATA_HOME" ), resourcesDir.GetPath() );
139 if( !m_runFromBuildDirWasSet )
140 wxSetEnv( wxS(
"KICAD_RUN_FROM_BUILD_DIR" ), wxS(
"1" ) );
146 if( m_runFromBuildDirWasSet )
147 wxSetEnv( wxS(
"KICAD_RUN_FROM_BUILD_DIR" ), m_oldRunFromBuildDir );
149 wxUnsetEnv( wxS(
"KICAD_RUN_FROM_BUILD_DIR" ) );
151#if defined( __WXMAC__ ) || defined( __WXMSW__ )
152 if( m_stockDataHomeWasSet )
153 wxSetEnv( wxS(
"KICAD_STOCK_DATA_HOME" ), m_oldStockDataHome );
155 wxUnsetEnv( wxS(
"KICAD_STOCK_DATA_HOME" ) );
159 bool m_runFromBuildDirWasSet =
false;
160 wxString m_oldRunFromBuildDir;
161#if defined( __WXMAC__ ) || defined( __WXMSW__ )
162 bool m_stockDataHomeWasSet =
false;
163 wxString m_oldStockDataHome;
170BOOST_FIXTURE_TEST_SUITE( RemoteProviderClientTests, BUILD_DIR_FIXTURE )
174 std::vector<wxString> requestedUrls;
180 wxUnusedVar( aError );
181 requestedUrls.push_back( aRequest.
url );
183 aResponse.
body = dumpJson( providerMetadataJson() );
190 BOOST_REQUIRE( client.DiscoverProvider( wxString(
"https://provider.example.test" ), metadata, error ) );
193 wxString(
"https://provider.example.test/.well-known/kicad-remote-provider" ) );
205 wxUnusedVar( aError );
207 wxString(
"https://provider.example.test/.well-known/oauth-authorization-server" ) );
209 aResponse.
body = dumpJson( authServerMetadataJson() );
216 BOOST_REQUIRE( client.FetchOAuthServerMetadata( metadata, authMetadata, error ) );
218 wxString(
"https://provider.example.test/oauth/authorize" ) );
219 BOOST_CHECK_EQUAL( authMetadata.token_endpoint, wxString(
"https://provider.example.test/oauth/token" ) );
230 wxUnusedVar( aError );
233 wxString(
"https://provider.example.test/api/v1/parts/acme-res-10k" ) );
235 aResponse.
body = dumpJson( manifestJson() );
242 BOOST_REQUIRE( client.FetchManifest( metadata, wxString(
"acme-res-10k" ), wxString(), manifest, error ) );
244 BOOST_REQUIRE_EQUAL( manifest.assets.size(), 2U );
247 wxString(
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" ) );
248 BOOST_CHECK( manifest.assets.front().required );
256 oauth.
token_endpoint = wxString(
"https://provider.example.test/oauth/token" );
262 wxUnusedVar( aError );
265 BOOST_CHECK( aRequest.
body.Contains( wxString(
"grant_type=authorization_code" ) ) );
266 BOOST_CHECK( aRequest.
body.Contains( wxString(
"code=test-code" ) ) );
268 aResponse.
body = dumpJson( {
269 {
"access_token",
"access-123" },
270 {
"refresh_token",
"refresh-123" },
271 {
"token_type",
"Bearer" },
272 {
"scope",
"openid parts.read" },
273 {
"expires_in", 3600 }
280 session.redirect_uri = wxString(
"http://127.0.0.1:9000/oauth/callback" );
281 session.code_verifier = wxString(
"verifier" );
285 BOOST_REQUIRE( client.ExchangeAuthorizationCode( oauth, session, wxString(
"test-code" ), tokens, error ) );
294 oauth.
token_endpoint = wxString(
"https://provider.example.test/oauth/token" );
300 wxUnusedVar( aError );
301 BOOST_CHECK( aRequest.
body.Contains( wxString(
"grant_type=refresh_token" ) ) );
302 BOOST_CHECK( aRequest.
body.Contains( wxString(
"refresh_token=refresh-123" ) ) );
304 aResponse.
body = dumpJson( {
305 {
"access_token",
"access-456" },
306 {
"refresh_token",
"refresh-456" },
307 {
"token_type",
"Bearer" },
308 {
"scope",
"openid parts.read" },
309 {
"expires_in", 3600 }
316 BOOST_REQUIRE( client.RefreshAccessToken( oauth, wxString(
"kicad-desktop" ),
317 wxString(
"refresh-123" ), tokens, error ) );
331 wxUnusedVar( aError );
332 BOOST_CHECK( aRequest.
body.Contains( wxString(
"token=access-123" ) ) );
334 aResponse.
body = wxString(
"{}" );
339 BOOST_CHECK( client.RevokeToken( oauth, wxString(
"kicad-desktop" ), wxString(
"access-123" ), error ) );
REMOTE_PROVIDER_HTTP_METHOD method
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(DiscoveryFetchesWellKnownMetadata)
BOOST_CHECK_EQUAL(result, "25.4")