57
result.Verify({.Stdout = L"line1\nline2\n", .Stderr = L"", .ExitCode = 0});
58
}
59
60
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_Timestamps)
61
+ {
62
+ // Run a container that outputs a line
63
+ auto result =
64
+ RunWslc(std::format(L"container run --name {} {} sh -c \"echo hello\"", WslcContainerName, DebianImage.NameAndTag()));
65
+ result.Verify({.Stderr = L"", .ExitCode = 0});
66
+
67
+ // Verify --timestamps adds timestamp prefix to log lines
68
+ result = RunWslc(std::format(L"container logs --timestamps {}", WslcContainerName));
69
+ result.Verify({.Stderr = L"", .ExitCode = 0});
70
+
71
+ // Timestamps should be in RFC3339 format (e.g. "2024-01-01T00:00:00.000000000Z hello")
72
+ // Verify output contains a timestamp-like prefix followed by the message
73
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"hello"));
74
+ auto lines = result.GetStdoutLines();
75
+ VERIFY_IS_TRUE(!lines.empty());
76
+ // A timestamp line should have at least a 'T' character (ISO 8601 separator)
77
+ VERIFY_IS_TRUE(lines[0].find(L'T') != std::wstring::npos);
78
+ }
79
+
80
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_TimestampsShortFlag)
81
+ {
82
+ // Run a container that outputs a line
83
+ auto result =
84
+ RunWslc(std::format(L"container run --name {} {} sh -c \"echo world\"", WslcContainerName, DebianImage.NameAndTag()));
85
+ result.Verify({.Stderr = L"", .ExitCode = 0});
86
+
87
+ // Verify -t (short for --timestamps) works the same way
88
+ result = RunWslc(std::format(L"container logs -t {}", WslcContainerName));
89
+ result.Verify({.Stderr = L"", .ExitCode = 0});
90
+
91
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"world"));
92
+ auto lines = result.GetStdoutLines();
93
+ VERIFY_IS_TRUE(!lines.empty());
94
+ VERIFY_IS_TRUE(lines[0].find(L'T') != std::wstring::npos);
95
+ }
96
+
97
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_Since)
98
+ {
99
+ // Run a container that outputs a line
100
+ auto result =
101
+ RunWslc(std::format(L"container run --name {} {} sh -c \"echo before\"", WslcContainerName, DebianImage.NameAndTag()));
102
+ result.Verify({.Stderr = L"", .ExitCode = 0});
103
+
104
+ // Using --since 0 should return all logs (epoch start)
105
+ result = RunWslc(std::format(L"container logs --since 0 {}", WslcContainerName));
106
+ result.Verify({.Stderr = L"", .ExitCode = 0});
107
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"before"));
108
+
109
+ // Using --since with a far-future timestamp should return no logs
110
+ result = RunWslc(std::format(L"container logs --since 9999999999 {}", WslcContainerName));
111
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
112
+ }
113
+
114
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_Until)
115
+ {
116
+ // Run a container that outputs a line
117
+ auto result =
118
+ RunWslc(std::format(L"container run --name {} {} sh -c \"echo test_until\"", WslcContainerName, DebianImage.NameAndTag()));
119
+ result.Verify({.Stderr = L"", .ExitCode = 0});
120
+
121
+ // Using --until with a far-future timestamp should return all logs
122
+ result = RunWslc(std::format(L"container logs --until 9999999999 {}", WslcContainerName));
123
+ result.Verify({.Stderr = L"", .ExitCode = 0});
124
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"test_until"));
125
+
126
+ // Using --until 1 (epoch + 1 second) should return no logs since container was created much later
127
+ result = RunWslc(std::format(L"container logs --until 1 {}", WslcContainerName));
128
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
129
+ }
130
+
131
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_SinceAndUntilCombined)
132
+ {
133
+ // Run a container that outputs a line
134
+ auto result =
135
+ RunWslc(std::format(L"container run --name {} {} sh -c \"echo combined\"", WslcContainerName, DebianImage.NameAndTag()));
136
+ result.Verify({.Stderr = L"", .ExitCode = 0});
137
+
138
+ // Using --since 0 --until far-future should return all logs
139
+ result = RunWslc(std::format(L"container logs --since 0 --until 9999999999 {}", WslcContainerName));
140
+ result.Verify({.Stderr = L"", .ExitCode = 0});
141
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"combined"));
142
+
143
+ // Using --since far-future --until far-future should return no logs
144
+ result = RunWslc(std::format(L"container logs --since 9999999999 --until 9999999999 {}", WslcContainerName));
145
+ result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
146
+ }
147
+
148
+ WSLC_TEST_METHOD(WSLCE2E_Container_Logs_AllOptionsCombined)
149
+ {
150
+ // Run a container that outputs multiple lines
151
+ auto result = RunWslc(std::format(
152
+ L"container run --name {} {} sh -c \"echo a && echo b && echo c\"", WslcContainerName, DebianImage.NameAndTag()));
153
+ result.Verify({.Stderr = L"", .ExitCode = 0});
154
+
155
+ // Combine --timestamps --since 0 --tail 2
156
+ result = RunWslc(std::format(L"container logs --timestamps --since 0 --tail 2 {}", WslcContainerName));
157
+ result.Verify({.Stderr = L"", .ExitCode = 0});
158
+
159
+ // Should have at most 2 lines (from --tail 2) and each should have a timestamp
160
+ auto lines = result.GetStdoutLines();
161
+ VERIFY_IS_TRUE(lines.size() <= 2);
162
+ for (const auto& line : lines)
163
+ {
164
+ if (!line.empty())
165
+ {
166
+ VERIFY_IS_TRUE(line.find(L'T') != std::wstring::npos);
167
+ }
168
+ }
169
+ }
170
+
171
private:
172
const std::wstring WslcContainerName = L"wslc-test-logs";
173
const TestImage& DebianImage = DebianTestImage();