@@ -34,6 +34,11 @@ struct Fixture {
3434 ~Fixture () { std::remove (path.c_str ()); }
3535};
3636
37+ struct TagFilterFixture {
38+ std::string path = tsfile_cli_test::write_tag_filter_fixture();
39+ ~TagFilterFixture () { std::remove (path.c_str ()); }
40+ };
41+
3742size_t count_lines (const std::string& s) {
3843 size_t n = 0 ;
3944 for (char c : s) {
@@ -134,6 +139,28 @@ TEST(CliE2E, CatReturnsAllRows) {
134139 EXPECT_NE (out.str ().find (" time\t s1\n " ), std::string::npos);
135140}
136141
142+ TEST (CliE2E, CatPushesDownOffsetAndLimit) {
143+ Fixture f;
144+ std::ostringstream out;
145+ std::ostringstream err;
146+ int code = tsfile_cli::run_cli (
147+ {" cat" , " -m" , " s1" , " --offset" , " 2" , " -n" , " 2" , " -f" , " tsv" , f.path },
148+ out, err);
149+ EXPECT_EQ (code, 0 );
150+ EXPECT_EQ (out.str (), " time\t s1\n 2\t 20\n 3\t 30\n " );
151+ }
152+
153+ TEST (CliE2E, HeadPushesDownOffsetAndLimit) {
154+ Fixture f;
155+ std::ostringstream out;
156+ std::ostringstream err;
157+ int code = tsfile_cli::run_cli (
158+ {" head" , " -m" , " s1" , " --offset" , " 1" , " -n" , " 3" , " -f" , " tsv" , f.path },
159+ out, err);
160+ EXPECT_EQ (code, 0 );
161+ EXPECT_EQ (out.str (), " time\t s1\n 1\t 10\n 2\t 20\n 3\t 30\n " );
162+ }
163+
137164TEST (CliE2E, CatWithTimeRange) {
138165 Fixture f;
139166 std::ostringstream out;
@@ -145,6 +172,65 @@ TEST(CliE2E, CatWithTimeRange) {
145172 EXPECT_EQ (out.str (), " time\t s1\n 2\t 20\n 3\t 30\n " );
146173}
147174
175+ TEST (CliE2E, CatAppliesOffsetAfterTimeRange) {
176+ Fixture f;
177+ std::ostringstream out;
178+ std::ostringstream err;
179+ int code =
180+ tsfile_cli::run_cli ({" cat" , " -m" , " s1" , " --start" , " 1" , " --end" , " 4" ,
181+ " --offset" , " 1" , " -n" , " 2" , " -f" , " tsv" , f.path },
182+ out, err);
183+ EXPECT_EQ (code, 0 );
184+ EXPECT_EQ (out.str (), " time\t s1\n 2\t 20\n 3\t 30\n " );
185+ }
186+
187+ TEST (CliE2E, CatFiltersRowsByTagEq) {
188+ TagFilterFixture f;
189+ std::ostringstream out;
190+ std::ostringstream err;
191+ int code = tsfile_cli::run_cli ({" cat" , " -m" , " s1" , " --tag-filter" , " id1" ,
192+ " eq" , " dev_b" , " -f" , " tsv" , f.path },
193+ out, err);
194+ EXPECT_EQ (code, 0 ) << err.str ();
195+ EXPECT_EQ (out.str (), " time\t s1\n 1\t 20\n 2\t 30\n " );
196+ }
197+
198+ TEST (CliE2E, HeadFiltersRowsByTagBetween) {
199+ TagFilterFixture f;
200+ std::ostringstream out;
201+ std::ostringstream err;
202+ int code =
203+ tsfile_cli::run_cli ({" head" , " -m" , " s1" , " --tag-between" , " id1" ,
204+ " dev_b" , " dev_c" , " -n" , " 10" , " -f" , " tsv" , f.path },
205+ out, err);
206+ EXPECT_EQ (code, 0 ) << err.str ();
207+ EXPECT_EQ (out.str (), " time\t s1\n 1\t 20\n 2\t 30\n 3\t 40\n " );
208+ }
209+
210+ TEST (CliE2E, SampleFiltersRowsByTagEq) {
211+ TagFilterFixture f;
212+ std::ostringstream out;
213+ std::ostringstream err;
214+ int code = tsfile_cli::run_cli (
215+ {" sample" , " -m" , " s1" , " --tag-filter" , " id1" , " eq" , " dev_b" , " -n" , " 10" ,
216+ " --seed" , " 1" , " -f" , " tsv" , f.path },
217+ out, err);
218+ EXPECT_EQ (code, 0 ) << err.str ();
219+ EXPECT_EQ (out.str (), " time\t s1\n 1\t 20\n 2\t 30\n " );
220+ }
221+
222+ TEST (CliE2E, TagFilterRejectsFieldColumn) {
223+ TagFilterFixture f;
224+ std::ostringstream out;
225+ std::ostringstream err;
226+ int code = tsfile_cli::run_cli ({" cat" , " -m" , " s1" , " --tag-filter" , " s1" ,
227+ " eq" , " 20" , " -f" , " tsv" , f.path },
228+ out, err);
229+ EXPECT_EQ (code, 1 );
230+ EXPECT_NE (err.str ().find (" invalid tag filter column" ), std::string::npos)
231+ << err.str ();
232+ }
233+
148234TEST (CliE2E, CatJsonIsNdjson) {
149235 Fixture f;
150236 std::ostringstream out;
@@ -181,6 +267,39 @@ TEST(CliE2E, CountReportsSeriesCountsAndTotal) {
181267 EXPECT_NE (out.str ().find (" total\t\t " ), std::string::npos);
182268}
183269
270+ TEST (CliE2E, MetadataTableFilterIsCaseInsensitive) {
271+ Fixture f;
272+
273+ std::ostringstream schema_out;
274+ std::ostringstream schema_err;
275+ EXPECT_EQ (
276+ tsfile_cli::run_cli ({" schema" , " -t" , " TABLE1" , " -f" , " tsv" , f.path },
277+ schema_out, schema_err),
278+ 0 );
279+ EXPECT_NE (schema_out.str ().find (" table1\t s1\t INT64" ), std::string::npos)
280+ << schema_out.str ();
281+
282+ std::ostringstream count_out;
283+ std::ostringstream count_err;
284+ EXPECT_EQ (
285+ tsfile_cli::run_cli ({" count" , " -t" , " TABLE1" , " -f" , " tsv" , f.path },
286+ count_out, count_err),
287+ 0 );
288+ EXPECT_NE (count_out.str ().find (" table1.id1_field_1.id2_field_2\t s1\t 5" ),
289+ std::string::npos)
290+ << count_out.str ();
291+
292+ std::ostringstream stats_out;
293+ std::ostringstream stats_err;
294+ EXPECT_EQ (
295+ tsfile_cli::run_cli ({" stats" , " -t" , " TABLE1" , " -f" , " tsv" , f.path },
296+ stats_out, stats_err),
297+ 0 );
298+ EXPECT_NE (stats_out.str ().find (" table1.id1_field_1.id2_field_2\t s1\t 5" ),
299+ std::string::npos)
300+ << stats_out.str ();
301+ }
302+
184303TEST (CliE2E, SampleIsReproducibleWithSeed) {
185304 Fixture f;
186305 std::ostringstream out1;
0 commit comments