Skip to content

Commit 6be2b55

Browse files
author
AndrewMorgan1
committed
Added CLI based unit tests
1 parent 6b73b30 commit 6be2b55

4 files changed

Lines changed: 316 additions & 4 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System.Text.Json;
2+
using TokenKit.CLI;
3+
4+
namespace TokenKit.Tests.CLI
5+
{
6+
public class ConsoleStylerTests
7+
{
8+
private readonly TextWriter _originalOut;
9+
10+
public ConsoleStylerTests()
11+
{
12+
_originalOut = Console.Out;
13+
}
14+
15+
private static string CaptureConsoleOutput(Action action)
16+
{
17+
using var sw = new StringWriter();
18+
Console.SetOut(sw);
19+
action();
20+
Console.Out.Flush();
21+
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
22+
return sw.ToString();
23+
}
24+
25+
[Fact]
26+
public void WriteSuccess_ShouldWriteGreenMessage()
27+
{
28+
var output = CaptureConsoleOutput(() =>
29+
ConsoleStyler.WriteSuccess("Success message"));
30+
31+
Assert.Contains("✅", output);
32+
Assert.Contains("Success message", output);
33+
}
34+
35+
[Fact]
36+
public void WriteWarning_ShouldWriteYellowMessage()
37+
{
38+
var output = CaptureConsoleOutput(() =>
39+
ConsoleStyler.WriteWarning("Be careful!"));
40+
41+
Assert.Contains("⚠️", output);
42+
Assert.Contains("Be careful!", output);
43+
}
44+
45+
[Fact]
46+
public void WriteError_ShouldWriteRedMessage()
47+
{
48+
var output = CaptureConsoleOutput(() =>
49+
ConsoleStyler.WriteError("Something went wrong"));
50+
51+
Assert.Contains("❌", output);
52+
Assert.Contains("Something went wrong", output);
53+
}
54+
55+
[Fact]
56+
public void WriteInfo_ShouldWritePlainCyanMessage()
57+
{
58+
var output = CaptureConsoleOutput(() =>
59+
ConsoleStyler.WriteInfo("Info line"));
60+
61+
Assert.Contains("Info line", output);
62+
}
63+
64+
[Fact]
65+
public void WriteJson_ShouldSerializeObject()
66+
{
67+
var obj = new { Message = "Hello", Count = 3 };
68+
69+
var output = CaptureConsoleOutput(() =>
70+
ConsoleStyler.WriteJson(obj));
71+
72+
// Trim and find JSON start (skip emojis or color codes)
73+
var jsonStart = output.IndexOf('{');
74+
output = jsonStart >= 0 ? output.Substring(jsonStart) : output.Trim();
75+
76+
using var doc = JsonDocument.Parse(output);
77+
var root = doc.RootElement;
78+
Assert.Equal("Hello", root.GetProperty("Message").GetString());
79+
Assert.Equal(3, root.GetProperty("Count").GetInt32());
80+
}
81+
82+
[Fact]
83+
public void WriteJson_ShouldSupportWarningColorFlag()
84+
{
85+
var obj = new { Status = "Partial" };
86+
87+
var output = CaptureConsoleOutput(() =>
88+
ConsoleStyler.WriteJson(obj, success: false));
89+
90+
Assert.Contains("Partial", output);
91+
}
92+
}
93+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using TokenKit.CLI;
2+
3+
namespace TokenKit.Tests.CLI
4+
{
5+
public class LoggerTests : IDisposable
6+
{
7+
private readonly string _logFile;
8+
private readonly TextWriter _originalOut;
9+
10+
public LoggerTests()
11+
{
12+
_originalOut = Console.Out;
13+
_logFile = Path.Combine(AppContext.BaseDirectory, "tokenkit.log");
14+
if (File.Exists(_logFile))
15+
File.Delete(_logFile);
16+
Logger.QuietMode = false;
17+
}
18+
19+
public void Dispose()
20+
{
21+
// restore console and quiet mode after each test
22+
Console.SetOut(_originalOut);
23+
Logger.QuietMode = false;
24+
}
25+
26+
private static string CaptureConsoleOutput(Action action)
27+
{
28+
using var sw = new StringWriter();
29+
Console.SetOut(sw);
30+
action();
31+
Console.Out.Flush();
32+
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
33+
return sw.ToString();
34+
}
35+
36+
[Fact]
37+
public void Info_Warn_Error_Success_ShouldWriteToFile_AndConsole()
38+
{
39+
// Act
40+
var output = CaptureConsoleOutput(() =>
41+
{
42+
Logger.Info("info test");
43+
Logger.Warn("warn test");
44+
Logger.Error("error test");
45+
Logger.Success("success test");
46+
});
47+
48+
// Assert console output
49+
Assert.Contains("info test", output);
50+
Assert.Contains("warn test", output);
51+
Assert.Contains("error test", output);
52+
Assert.Contains("success test", output);
53+
54+
// Assert file contents
55+
Assert.True(File.Exists(_logFile));
56+
var log = File.ReadAllText(_logFile);
57+
Assert.Contains("INFO", log);
58+
Assert.Contains("WARN", log);
59+
Assert.Contains("ERROR", log);
60+
Assert.Contains("SUCCESS", log);
61+
}
62+
63+
[Fact]
64+
public void ShouldRespectQuietMode_AndStillWriteFile()
65+
{
66+
Logger.QuietMode = true;
67+
68+
var output = CaptureConsoleOutput(() =>
69+
{
70+
Logger.Info("quiet test");
71+
});
72+
73+
Assert.True(string.IsNullOrWhiteSpace(output)); // no console output
74+
Assert.True(File.Exists(_logFile));
75+
Assert.Contains("quiet test", File.ReadAllText(_logFile));
76+
}
77+
78+
[Fact]
79+
public void ShouldTruncateLog_WhenExceedsOneMB()
80+
{
81+
// Arrange — create a large dummy log file
82+
var largeLines = string.Join(Environment.NewLine, Enumerable.Repeat("X", 150000));
83+
File.WriteAllText(_logFile, largeLines);
84+
85+
// Act
86+
Logger.Info("trigger truncate");
87+
88+
// Assert file shrinks roughly in half
89+
var newSize = new FileInfo(_logFile).Length;
90+
Assert.True(newSize < 1_000_000);
91+
Assert.Contains("trigger truncate", File.ReadAllText(_logFile));
92+
}
93+
}
94+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using TokenKit.CLI;
2+
3+
namespace TokenKit.Tests.CLI
4+
{
5+
public class TokenKitCLITests : IDisposable
6+
{
7+
private readonly TextWriter _originalOut;
8+
9+
public TokenKitCLITests()
10+
{
11+
_originalOut = Console.Out;
12+
Logger.QuietMode = false;
13+
}
14+
15+
public void Dispose()
16+
{
17+
Logger.QuietMode = false;
18+
Console.SetOut(_originalOut);
19+
}
20+
21+
private static string CaptureConsole(Func<Task> func)
22+
{
23+
using var sw = new StringWriter();
24+
Console.SetOut(sw);
25+
func().GetAwaiter().GetResult();
26+
Console.Out.Flush();
27+
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
28+
return sw.ToString();
29+
}
30+
31+
// -----------------------------
32+
33+
[Fact]
34+
public void RunAsync_ShouldDisplayHelp_WhenNoArgs()
35+
{
36+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(Array.Empty<string>()));
37+
Assert.Contains("TokenKit", output);
38+
}
39+
40+
[Fact]
41+
public void RunAsync_ShouldHandleUnknownCommand()
42+
{
43+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "invalidcmd" }));
44+
Assert.Contains("Unknown", output);
45+
}
46+
47+
[Fact]
48+
public void RunAsync_ShouldSupportJsonAndQuietModes()
49+
{
50+
// JSON mode should suppress banner output but still run safely
51+
var outputJson = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "--json" }));
52+
Assert.False(string.IsNullOrWhiteSpace(outputJson)); // some output expected (help or message)
53+
54+
// Quiet mode should explicitly mention quiet mode
55+
var outputQuiet = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "--quiet" }));
56+
Assert.Contains("quiet mode", outputQuiet, StringComparison.OrdinalIgnoreCase);
57+
}
58+
59+
[Fact]
60+
public void RunAsync_ShouldShowVersion_AndHelpShort()
61+
{
62+
var output1 = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "--version" }));
63+
var output2 = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "--help", "short" }));
64+
65+
Assert.Contains("version", output1, StringComparison.OrdinalIgnoreCase);
66+
Assert.Contains("Usage:", output2);
67+
}
68+
69+
[Fact]
70+
public void RunAsync_ShouldListModels_Default()
71+
{
72+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "models", "list" }));
73+
Assert.Contains("Registered Models", output);
74+
}
75+
76+
[Fact]
77+
public void RunAsync_ShouldHandleModelsUnknownSubcommand()
78+
{
79+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "models", "unknown" }));
80+
Assert.Contains("Unknown subcommand", output);
81+
}
82+
83+
[Fact]
84+
public void RunAsync_ShouldHandleModelsNoSubcommand()
85+
{
86+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "models" }));
87+
Assert.Contains("Missing subcommand", output);
88+
}
89+
90+
[Fact]
91+
public void RunAsync_ShouldShowVersionFlag()
92+
{
93+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "-v" }));
94+
Assert.Contains("TokenKit", output);
95+
}
96+
97+
[Fact]
98+
public void RunAsync_ShouldShowHelpFlag()
99+
{
100+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "-h" }));
101+
Assert.Contains("Usage:", output);
102+
}
103+
104+
[Fact]
105+
public void RunAsync_ShouldTriggerScrapeModelsSafely()
106+
{
107+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "scrape-models" }));
108+
Assert.Contains("OpenAI model data", output);
109+
}
110+
111+
[Fact]
112+
public void RunAsync_ShouldTriggerUpdateModelsSafely()
113+
{
114+
var output = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "update-models" }));
115+
Assert.Contains("Model data updated", output);
116+
}
117+
118+
[Fact]
119+
public void RunAsync_ShouldTriggerAnalyzeAndValidateSafely()
120+
{
121+
var analyzeOutput = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "analyze", "Hello", "--model", "gpt-4o" }));
122+
var validateOutput = CaptureConsole(() => TokenKitCLI.RunAsync(new[] { "validate", "Hi", "--model", "gpt-4o" }));
123+
124+
Assert.Contains("TokenCount", analyzeOutput);
125+
Assert.Contains("Model", validateOutput);
126+
}
127+
}
128+
}
129+

tests/TokenKit.Tests/TokenKit.Tests.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,4 @@
3030
<Using Include="Xunit" />
3131
</ItemGroup>
3232

33-
<ItemGroup>
34-
<Folder Include="CLI\" />
35-
</ItemGroup>
36-
3733
</Project>

0 commit comments

Comments
 (0)