-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implements the TestPurchase scenario in Go
- Loading branch information
1 parent
0efff47
commit 8440bfc
Showing
2 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package endtoend_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"testing" | ||
"time" | ||
|
||
"github.com/canonical/ubuntu-pro-for-windows/mocks/contractserver/contractsmockserver" | ||
"github.com/canonical/ubuntu-pro-for-windows/mocks/storeserver/storemockserver" | ||
"github.com/stretchr/testify/require" | ||
wsl "github.com/ubuntu/gowsl" | ||
"golang.org/x/exp/slog" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
const ( | ||
contractsEndpointEnv = "UP4W_CONTRACTS_BACKEND_MOCK_ENDPOINT" | ||
storeEndpointEnv = "UP4W_MS_STORE_MOCK_ENDPOINT" | ||
allowPurchaseEnvOverride = "UP4W_ALLOW_STORE_PURCHASE=1" | ||
) | ||
|
||
func TestPurchase(t *testing.T) { | ||
type whenToken int | ||
const ( | ||
never whenToken = iota | ||
beforeDistroRegistration | ||
afterDistroRegistration | ||
) | ||
|
||
// Let's be lazy and don't fall into the risk of changing the function name without updating the places where its name is used. | ||
currentFuncName := t.Name() | ||
|
||
testCases := map[string]struct { | ||
whenToStartAgent whenToken | ||
withToken string | ||
csServerDown bool | ||
storeDown bool | ||
|
||
wantAttached bool | ||
}{ | ||
"Success when applying pro token before registration": {whenToStartAgent: beforeDistroRegistration, wantAttached: true}, | ||
"Success when applying pro token after registration": {whenToStartAgent: afterDistroRegistration, wantAttached: true}, | ||
|
||
"Error due MS Store API failure": {whenToStartAgent: beforeDistroRegistration, storeDown: true}, | ||
"Error due Contracts Server Backend down": {whenToStartAgent: afterDistroRegistration, csServerDown: true}, | ||
} | ||
|
||
for name, tc := range testCases { | ||
tc := tc | ||
t.Run(name, func(t *testing.T) { | ||
testSetup(t) | ||
|
||
ctx := context.Background() | ||
contractsCtx, contractsCancel := context.WithCancel(ctx) | ||
defer contractsCancel() | ||
|
||
settings := contractsmockserver.DefaultSettings() | ||
|
||
if len(tc.withToken) > 0 { | ||
settings.Subscription.OnSuccess.Value = tc.withToken | ||
} else { | ||
token := os.Getenv(proTokenEnv) | ||
if len(token) == 0 { | ||
slog.Error("UP4W_TEST_PRO_TOKEN environment variable must be set to a valid Pro Token") | ||
os.Exit(1) | ||
} | ||
settings.Subscription.OnSuccess.Value = token | ||
} | ||
cs := contractsmockserver.NewServer(settings) | ||
if !tc.csServerDown { | ||
err := cs.Serve(contractsCtx, "localhost:0") | ||
require.NoError(t, err, "Setup: Server should return no error") | ||
} | ||
contractsEndpointEnvOverride := fmt.Sprintf("%s=%s", contractsEndpointEnv, cs.Address()) | ||
//nolint:errcheck // Nothing we can do about it | ||
defer cs.Stop() | ||
storeCtx, storeCancel := context.WithCancel(ctx) | ||
defer storeCancel() | ||
|
||
storeSettings := storemockserver.DefaultSettings() | ||
|
||
testData, err := os.ReadFile("testdata/TestPurchase/storemock_config.yaml") | ||
if err != nil { | ||
slog.Error(fmt.Sprintf("Could not read input file: %v", err)) | ||
os.Exit(1) | ||
} | ||
|
||
yaml.Unmarshal(testData, &storeSettings) | ||
|
||
store := storemockserver.NewServer(storeSettings) | ||
if !tc.storeDown { | ||
err = store.Serve(storeCtx, "localhost:0") | ||
require.NoError(t, err, "Setup: Server should return no error") | ||
} | ||
storeEndpointEnvOverride := fmt.Sprintf("%s=%s", storeEndpointEnv, store.Address()) | ||
//nolint:errcheck // Nothing we can do about it | ||
defer store.Stop() | ||
|
||
// Either runs the ubuntupro app before... | ||
if tc.whenToStartAgent == beforeDistroRegistration { | ||
cleanup := startAgent(t, ctx, currentFuncName, allowPurchaseEnvOverride, contractsEndpointEnvOverride, storeEndpointEnvOverride) | ||
defer cleanup() | ||
} | ||
|
||
// Distro setup | ||
name := registerFromTestImage(t, ctx) | ||
d := wsl.NewDistro(ctx, name) | ||
|
||
defer func() { | ||
if t.Failed() { | ||
logWslProServiceJournal(t, ctx, d) | ||
} | ||
}() | ||
|
||
out, err := d.Command(ctx, "exit 0").CombinedOutput() | ||
require.NoErrorf(t, err, "Setup: could not wake distro up: %v. %s", err, out) | ||
|
||
// ... or after registration, but never both. | ||
if tc.whenToStartAgent == afterDistroRegistration { | ||
cleanup := startAgent(t, ctx, currentFuncName, allowPurchaseEnvOverride, contractsEndpointEnvOverride, storeEndpointEnvOverride) | ||
defer cleanup() | ||
|
||
out, err = d.Command(ctx, "exit 0").CombinedOutput() | ||
require.NoErrorf(t, err, "Setup: could not wake distro up: %v. %s", err, out) | ||
} | ||
|
||
const maxTimeout = 30 * time.Second | ||
|
||
if !tc.wantAttached { | ||
time.Sleep(maxTimeout) | ||
proCtx, cancel := context.WithTimeout(ctx, maxTimeout) | ||
defer cancel() | ||
attached, err := distroIsProAttached(t, proCtx, d) | ||
require.NoError(t, err, "could not determine if distro is attached") | ||
require.False(t, attached, "distro should not have been Pro attached") | ||
return | ||
} | ||
|
||
require.Eventually(t, func() bool { | ||
attached, err := distroIsProAttached(t, ctx, d) | ||
if err != nil { | ||
t.Logf("could not determine if distro is attached: %v", err) | ||
} | ||
return attached | ||
}, maxTimeout, time.Second, "distro should have been Pro attached") | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
generateuserjwt: | ||
onsuccess: | ||
value: CPP_MOCK_JWT | ||
status: 200 | ||
disabled: false | ||
blocked: false | ||
getproducts: | ||
onsuccess: | ||
value: "" | ||
status: 200 | ||
disabled: false | ||
blocked: false | ||
purchase: | ||
onsuccess: | ||
value: "" | ||
status: 200 | ||
disabled: false | ||
blocked: false | ||
allproducts: | ||
- storeid: 9P25B50XMKXT | ||
title: Annual Subscription (production) | ||
description: To this lovely mock of the production product ID | ||
isinusercollection: false | ||
productkind: Durable | ||
expirationdate: 0001-01-01T00:00:00Z | ||
- storeid: 9N9Q5G4QSMLS | ||
title: Monthly Subscription (created for testing, free) | ||
description: To this lovely mock of the production product ID | ||
isinusercollection: false | ||
productkind: Durable | ||
expirationdate: 0001-01-01T00:00:00Z | ||
- storeid: CPP_MOCK_CONSUMABLE | ||
title: Consume | ||
description: This mock is nice | ||
isinusercollection: false | ||
productkind: Consumable | ||
expirationdate: 0001-01-01T00:00:00Z | ||
- storeid: cannotpurchase | ||
title: Forbidden | ||
description: This product cannot be owned | ||
isinusercollection: false | ||
productkind: Durable | ||
expirationdate: 0001-01-01T00:00:00Z | ||
- storeid: servererror | ||
title: Also forbidden | ||
description: This product always break the server | ||
isinusercollection: false | ||
productkind: Durable | ||
expirationdate: 0001-01-01T00:00:00Z |