master
cpp 153 lines 5.95 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCE2EPushPullTests.cpp
8
9 Abstract:
10
11 End-to-end tests for wslc image push and pull against a local registry.
12
13 --*/
14
15 #include "precomp.h"
16 #include "windows/Common.h"
17 #include "WSLCExecutor.h"
18 #include "WSLCE2EHelpers.h"
19 #include "TestImageRegistry.h"
20 #include "Argument.h"
21
22 namespace WSLCE2ETests {
23 using namespace wsl::shared;
24
25 class WSLCE2EPushPullTests
26 {
27 WSLC_TEST_CLASS(WSLCE2EPushPullTests)
28
29 WSLC_TEST_METHOD(WSLCE2E_Image_Push_HelpCommand)
30 {
31 auto result = RunWslc(L"image push --help");
32 result.Verify({.Stderr = L"", .ExitCode = 0});
33 VERIFY_IS_FALSE(result.Stdout.value().empty());
34 }
35
36 WSLC_TEST_METHOD(WSLCE2E_Image_Push_RootAlias)
37 {
38 auto result = RunWslc(L"push --help");
39 result.Verify({.Stderr = L"", .ExitCode = 0});
40 VERIFY_IS_FALSE(result.Stdout.value().empty());
41 }
42
43 WSLC_TEST_METHOD(WSLCE2E_Image_Pull_HelpCommand)
44 {
45 auto result = RunWslc(L"image pull --help");
46 result.Verify({.Stderr = L"", .ExitCode = 0});
47 VERIFY_IS_FALSE(result.Stdout.value().empty());
48 }
49
50 WSLC_TEST_METHOD(WSLCE2E_Image_Pull_RootAlias)
51 {
52 auto result = RunWslc(L"pull --help");
53 result.Verify({.Stderr = L"", .ExitCode = 0});
54 VERIFY_IS_FALSE(result.Stdout.value().empty());
55 }
56
57 WSLC_TEST_METHOD(WSLCE2E_Image_PushPull)
58 {
59 const auto& testImage = AlpineTestImage();
60 TestImageRegistry::Instance().EnsureLoaded(testImage);
61
62 // Start a local registry without auth.
63 auto session = OpenDefaultElevatedSession();
64
65 {
66 auto [registryContainer, registryAddress] = StartLocalRegistry(*session, "", "", 15003);
67 auto registryAddressW = string::MultiByteToWide(registryAddress);
68
69 // Tag the image for the local registry.
70 auto registryImage = TagImageForRegistry(testImage.NameAndTag(), registryAddressW);
71
72 auto tagCleanup = wil::scope_exit([&]() { RunWslc(std::format(L"image delete --force {}", registryImage)); });
73
74 // Standalone push/pull send progress to stdout (Docker parity), leaving stderr empty.
75 auto result = RunWslc(std::format(L"push {}", registryImage));
76 result.Verify({.Stderr = L"", .ExitCode = 0});
77 VERIFY_IS_TRUE(result.Stdout.has_value());
78 VERIFY_IS_FALSE(result.Stdout->empty());
79
80 // Delete the local copy and pull it back.
81 RunWslcAndVerify(std::format(L"image delete --force {}", registryImage), {.ExitCode = 0});
82
83 result = RunWslc(std::format(L"pull {}", registryImage));
84 result.Verify({.Stderr = L"", .ExitCode = 0});
85 VERIFY_IS_TRUE(result.Stdout.has_value());
86 VERIFY_IS_FALSE(result.Stdout->empty());
87
88 // Verify the image is now present.
89 auto registryRepo = registryImage.substr(0, registryImage.rfind(L':'));
90 result = RunWslc(L"image list --format json");
91 result.Verify({.Stderr = L"", .ExitCode = 0});
92 VERIFY_IS_TRUE(result.Stdout.has_value());
93 VERIFY_IS_TRUE(result.Stdout->find(registryRepo) != std::wstring::npos);
94 }
95 }
96
97 WSLC_TEST_METHOD(WSLCE2E_Image_Pull_QuietOption)
98 {
99 const auto& testImage = AlpineTestImage();
100 TestImageRegistry::Instance().EnsureLoaded(testImage);
101
102 auto session = OpenDefaultElevatedSession();
103
104 {
105 auto [registryContainer, registryAddress] = StartLocalRegistry(*session, "", "", 15004);
106 auto registryAddressW = string::MultiByteToWide(registryAddress);
107
108 // Tag and push the image so it can be pulled back from the registry.
109 auto registryImage = TagImageForRegistry(testImage.NameAndTag(), registryAddressW);
110 auto tagCleanup = wil::scope_exit([&]() { RunWslc(std::format(L"image delete --force {}", registryImage)); });
111
112 RunWslcAndVerify(std::format(L"push {}", registryImage), {.Stderr = L"", .ExitCode = 0});
113
114 // Delete the local copy so the pull actually fetches from the registry.
115 RunWslcAndVerify(std::format(L"image delete --force {}", registryImage), {.ExitCode = 0});
116
117 // Quiet pull (Docker parity): progress is suppressed and stdout is exactly the resolved canonical
118 // reference. The registry image is already fully-qualified, so it equals the printed reference.
119 // GetStdoutOneLine() also asserts there is exactly one output line, proving progress was suppressed.
120 auto result = RunWslc(std::format(L"pull --quiet {}", registryImage));
121 result.Verify({.Stderr = L"", .ExitCode = 0});
122 VERIFY_ARE_EQUAL(registryImage, result.GetStdoutOneLine());
123 }
124 }
125
126 WSLC_TEST_METHOD(WSLCE2E_Image_Push_NonExistentImage)
127 {
128 auto result = RunWslc(L"push does-not-exist:latest");
129 auto errorMessage = FormatErrorMessage(L"An image does not exist locally with the tag: does-not-exist", L"E_FAIL");
130 result.Verify({.Stderr = errorMessage, .ExitCode = 1});
131 }
132
133 WSLC_TEST_METHOD(WSLCE2E_Image_Pull_NonExistentImage)
134 {
135 auto result = RunWslc(L"pull does-not-exist:latest");
136 auto errorMessage = FormatErrorMessage(
137 L"pull access denied for does-not-exist, repository does not exist or may require 'docker login': denied: requested "
138 L"access to the resource is denied",
139 L"WSLC_E_IMAGE_NOT_FOUND");
140 result.Verify({.Stdout = L"", .Stderr = errorMessage, .ExitCode = 1});
141 }
142
143 WSLC_TEST_METHOD(WSLCE2E_Image_Pull_NameOnlyDefaultsTag)
144 {
145 auto result = RunWslc(L"pull does-not-exist");
146 result.Verify({.ExitCode = 1});
147 VERIFY_IS_TRUE(result.StdoutContainsLine(L"Using default tag: latest"));
148
149 // Quiet mode suppresses the "Using default tag" line, leaving stdout empty on failure.
150 RunWslcAndVerify(L"pull -q does-not-exist", {.Stdout = L"", .ExitCode = 1});
151 }
152 };
153 } // namespace WSLCE2ETests