add merchant instances/auth test case
This commit is contained in:
parent
98ab998a1e
commit
81be4aacd8
@ -94,6 +94,7 @@ import {
|
|||||||
TippingReserveStatus,
|
TippingReserveStatus,
|
||||||
TipCreateConfirmation,
|
TipCreateConfirmation,
|
||||||
TipCreateRequest,
|
TipCreateRequest,
|
||||||
|
MerchantInstancesResponse,
|
||||||
} from "./merchantApiTypes";
|
} from "./merchantApiTypes";
|
||||||
import { ApplyRefundResponse } from "@gnu-taler/taler-wallet-core";
|
import { ApplyRefundResponse } from "@gnu-taler/taler-wallet-core";
|
||||||
import { PendingOperationsResponse } from "@gnu-taler/taler-wallet-core";
|
import { PendingOperationsResponse } from "@gnu-taler/taler-wallet-core";
|
||||||
@ -1171,6 +1172,40 @@ export interface MerchantServiceInterface {
|
|||||||
readonly name: string;
|
readonly name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MerchantApiClient {
|
||||||
|
constructor(
|
||||||
|
private baseUrl: string,
|
||||||
|
private auth: MerchantAuthConfiguration,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
|
||||||
|
const baseUrl = this.baseUrl;
|
||||||
|
const url = new URL("private/auth", baseUrl);
|
||||||
|
await axios.post(url.href, auth, {
|
||||||
|
headers: this.makeAuthHeader(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInstances(): Promise<MerchantInstancesResponse> {
|
||||||
|
const url = new URL("private/instances", this.baseUrl);
|
||||||
|
const resp = await axios.get(url.href, {
|
||||||
|
headers: this.makeAuthHeader(),
|
||||||
|
});
|
||||||
|
return resp.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeAuthHeader(): Record<string, string> {
|
||||||
|
switch (this.auth.method) {
|
||||||
|
case "external":
|
||||||
|
return {};
|
||||||
|
case "token":
|
||||||
|
return {
|
||||||
|
Authorization: `Bearer ${this.auth.token}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export namespace MerchantPrivateApi {
|
export namespace MerchantPrivateApi {
|
||||||
export async function createOrder(
|
export async function createOrder(
|
||||||
merchantService: MerchantServiceInterface,
|
merchantService: MerchantServiceInterface,
|
||||||
@ -1407,8 +1442,9 @@ export class MerchantService implements MerchantServiceInterface {
|
|||||||
}
|
}
|
||||||
console.log("adding instance");
|
console.log("adding instance");
|
||||||
const url = `http://localhost:${this.merchantConfig.httpPort}/private/instances`;
|
const url = `http://localhost:${this.merchantConfig.httpPort}/private/instances`;
|
||||||
|
const auth = instanceConfig.auth ?? { method: "external" };
|
||||||
await axios.post(url, {
|
await axios.post(url, {
|
||||||
auth: { method: "external" },
|
auth,
|
||||||
payto_uris: instanceConfig.paytoUris,
|
payto_uris: instanceConfig.paytoUris,
|
||||||
id: instanceConfig.id,
|
id: instanceConfig.id,
|
||||||
name: instanceConfig.name,
|
name: instanceConfig.name,
|
||||||
@ -1443,8 +1479,13 @@ export class MerchantService implements MerchantServiceInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MerchantAuthConfiguration {
|
||||||
|
method: "external" | "token";
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MerchantInstanceConfig {
|
export interface MerchantInstanceConfig {
|
||||||
authToken?: string;
|
auth?: MerchantAuthConfiguration;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
paytoUris: string[];
|
paytoUris: string[];
|
||||||
|
@ -40,6 +40,7 @@ import {
|
|||||||
AmountString,
|
AmountString,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
CoinPublicKeyString,
|
CoinPublicKeyString,
|
||||||
|
EddsaPublicKeyString,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { codecForAmountString } from "@gnu-taler/taler-wallet-core/lib/util/amounts";
|
import { codecForAmountString } from "@gnu-taler/taler-wallet-core/lib/util/amounts";
|
||||||
|
|
||||||
@ -294,3 +295,25 @@ export interface TipCreateRequest {
|
|||||||
// will be included in the tip_token.
|
// will be included in the tip_token.
|
||||||
next_url: string;
|
next_url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface MerchantInstancesResponse {
|
||||||
|
// List of instances that are present in the backend (see Instance)
|
||||||
|
instances: MerchantInstanceDetail[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MerchantInstanceDetail {
|
||||||
|
// Merchant name corresponding to this instance.
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Merchant instance this response is about ($INSTANCE)
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
// Public key of the merchant/instance, in Crockford Base32 encoding.
|
||||||
|
merchant_pub: EddsaPublicKeyString;
|
||||||
|
|
||||||
|
// List of the payment targets supported by this instance. Clients can
|
||||||
|
// specify the desired payment target in /order requests. Note that
|
||||||
|
// front-ends do not have to support wallets selecting payment targets.
|
||||||
|
payment_targets: string[];
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021 Taler Systems S.A.
|
||||||
|
|
||||||
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports.
|
||||||
|
*/
|
||||||
|
import { URL } from "@gnu-taler/taler-wallet-core";
|
||||||
|
import axios from "axios";
|
||||||
|
import {
|
||||||
|
ExchangeService,
|
||||||
|
GlobalTestState,
|
||||||
|
MerchantApiClient,
|
||||||
|
MerchantService,
|
||||||
|
setupDb,
|
||||||
|
} from "./harness";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do basic checks on instance management and authentication.
|
||||||
|
*/
|
||||||
|
export async function runMerchantInstancesTest(t: GlobalTestState) {
|
||||||
|
// Set up test environment
|
||||||
|
|
||||||
|
const db = await setupDb(t);
|
||||||
|
|
||||||
|
const exchange = ExchangeService.create(t, {
|
||||||
|
name: "testexchange-1",
|
||||||
|
currency: "TESTKUDOS",
|
||||||
|
httpPort: 8081,
|
||||||
|
database: db.connStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
const merchant = await MerchantService.create(t, {
|
||||||
|
name: "testmerchant-1",
|
||||||
|
currency: "TESTKUDOS",
|
||||||
|
httpPort: 8083,
|
||||||
|
database: db.connStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
// We add the exchange to the config, but note that the exchange won't be started.
|
||||||
|
merchant.addExchange(exchange);
|
||||||
|
|
||||||
|
await merchant.start();
|
||||||
|
await merchant.pingUntilAvailable();
|
||||||
|
|
||||||
|
// Base URL for the default instance.
|
||||||
|
const baseUrl = merchant.makeInstanceBaseUrl();
|
||||||
|
|
||||||
|
{
|
||||||
|
const r = await axios.get(new URL("config", baseUrl).href);
|
||||||
|
console.log(r.data);
|
||||||
|
t.assertDeepEqual(r.data.currency, "TESTKUDOS");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instances should initially be empty
|
||||||
|
{
|
||||||
|
const r = await axios.get(new URL("private/instances", baseUrl).href);
|
||||||
|
t.assertDeepEqual(r.data.instances, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an instance, no auth!
|
||||||
|
await merchant.addInstance({
|
||||||
|
id: "default",
|
||||||
|
name: "Default Instance",
|
||||||
|
paytoUris: [`payto://x-taler-bank/merchant-default`],
|
||||||
|
auth: {
|
||||||
|
method: "external",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
|
||||||
|
method: "external",
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
const r = await merchantClient.getInstances();
|
||||||
|
t.assertDeepEqual(r.instances.length, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a "malformed" bearer Authorization header gets ignored
|
||||||
|
{
|
||||||
|
const url = merchant.makeInstanceBaseUrl();
|
||||||
|
const resp = await axios.get(new URL("private/instances", url).href, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": "foo bar-baz",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
t.assertDeepEqual(resp.status, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
await merchantClient.changeAuth({
|
||||||
|
method: "token",
|
||||||
|
token: "secret-token:foobar",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now this should fail, as we didn't change the auth of the client yet.
|
||||||
|
const exc = await t.assertThrowsAsync(async () => {
|
||||||
|
await merchantClient.getInstances();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.assertAxiosError(exc);
|
||||||
|
t.assertAxiosError(exc.response?.status === 401);
|
||||||
|
|
||||||
|
merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
|
||||||
|
method: "token",
|
||||||
|
token: "secret-token:foobar",
|
||||||
|
});
|
||||||
|
|
||||||
|
// With the new client auth settings, request should work again.
|
||||||
|
await merchantClient.getInstances();
|
||||||
|
|
||||||
|
// Now, try some variations.
|
||||||
|
|
||||||
|
{
|
||||||
|
const url = merchant.makeInstanceBaseUrl();
|
||||||
|
const resp = await axios.get(new URL("private/instances", url).href, {
|
||||||
|
headers: {
|
||||||
|
// Note the spaces
|
||||||
|
"Authorization": "Bearer secret-token:foobar",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.assertDeepEqual(resp.status, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runMerchantInstancesTest.suites = ["merchant"];
|
@ -59,6 +59,7 @@ import { runLibeufinRefundTest } from "./test-libeufin-refund";
|
|||||||
import { runLibeufinTutorialTest } from "./test-libeufin-tutorial";
|
import { runLibeufinTutorialTest } from "./test-libeufin-tutorial";
|
||||||
import { runDepositTest } from "./test-deposit";
|
import { runDepositTest } from "./test-deposit";
|
||||||
import CancellationToken from "cancellationtoken";
|
import CancellationToken from "cancellationtoken";
|
||||||
|
import { runMerchantInstancesTest } from "./test-merchant-instances";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test runner.
|
* Test runner.
|
||||||
@ -83,6 +84,7 @@ const allTests: TestMainFunction[] = [
|
|||||||
runLibeufinTutorialTest,
|
runLibeufinTutorialTest,
|
||||||
runLibeufinRefundTest,
|
runLibeufinRefundTest,
|
||||||
runMerchantExchangeConfusionTest,
|
runMerchantExchangeConfusionTest,
|
||||||
|
runMerchantInstancesTest,
|
||||||
runMerchantLongpollingTest,
|
runMerchantLongpollingTest,
|
||||||
runMerchantRefundApiTest,
|
runMerchantRefundApiTest,
|
||||||
runPayAbortTest,
|
runPayAbortTest,
|
||||||
|
Loading…
Reference in New Issue
Block a user