Skip to content

Commit 6c52392

Browse files
authored
Feature/test coverage improvements (#13)
This PR improves test coverage by replacing the resource manager pattern with a compile-time type-safe `ErrorFormatters` static class for error messages, adds comprehensive test suites for previously untested components (`DecimalSize`, `DatabaseTypeRequest`, `TypeGuessResult`, `GuessSettings`, `TypeDeciderFactory`, `PooledBuilder`, and various error handling scenarios), makes `GuessSettings` constructor and `PooledBuilderExample` class public for better testability, and includes minor infrastructure updates (GitHub workflow secret name change and `.gitignore` cleanup for Claude Code artifacts).
1 parent 33118df commit 6c52392

29 files changed

+3315
-411
lines changed

Tests/AdditionalCoverageTests.cs

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
using System;
2+
using System.Data;
3+
using System.Globalization;
4+
using NUnit.Framework;
5+
using System.Linq;
6+
7+
namespace TypeGuesser.Tests
8+
{
9+
[TestFixture]
10+
public class AdditionalCoverageTests
11+
{
12+
[Test]
13+
public void GetSharedFactory_WithVariousCultures_ReturnsCorrectFactories()
14+
{
15+
// Test different cultures to cover the factory caching logic
16+
var cultures = new[]
17+
{
18+
CultureInfo.InvariantCulture,
19+
new CultureInfo("en-US"),
20+
new CultureInfo("fr-FR"),
21+
new CultureInfo("de-DE"),
22+
new CultureInfo("ja-JP")
23+
};
24+
25+
foreach (var factory in cultures.Select(culture => Guesser.GetSharedFactory(culture)))
26+
{
27+
Assert.That(factory, Is.Not.Null);
28+
Assert.That(factory.Settings, Is.Not.Null);
29+
}
30+
}
31+
32+
[Test]
33+
public void GetSharedFactory_CachingBehavior_ReturnsSameInstances()
34+
{
35+
// Test that the same culture returns the same factory instance
36+
var culture = new CultureInfo("es-ES");
37+
var factory1 = Guesser.GetSharedFactory(culture);
38+
var factory2 = Guesser.GetSharedFactory(culture);
39+
var factory3 = Guesser.GetSharedFactory(culture);
40+
41+
Assert.That(factory1, Is.SameAs(factory2));
42+
Assert.That(factory2, Is.SameAs(factory3));
43+
}
44+
45+
[Test]
46+
public void DatabaseTypeRequest_Max_WithIncompatibleTypes_ThrowsNotSupportedException()
47+
{
48+
// Test the resource string path in DatabaseTypeRequest.Max
49+
var request1 = new DatabaseTypeRequest(typeof(Guid));
50+
var request2 = new DatabaseTypeRequest(typeof(Type));
51+
52+
var ex = Assert.Throws<NotSupportedException>(() => DatabaseTypeRequest.Max(request1, request2));
53+
Assert.That(ex.Message, Does.Contain("Could not combine Types"));
54+
}
55+
56+
[Test]
57+
public void Guesser_Dispose_MultipleCalls_Safe()
58+
{
59+
// Test that multiple dispose calls are safe
60+
using var guesser = new Guesser();
61+
guesser.AdjustToCompensateForValue("test");
62+
63+
guesser.Dispose();
64+
guesser.Dispose(); // Should not throw
65+
guesser.Dispose(); // Should not throw
66+
67+
Assert.Pass("Multiple dispose calls completed successfully");
68+
}
69+
70+
[Test]
71+
public void Guesser_DisposedAccess_ThrowsObjectDisposedException()
72+
{
73+
// Test accessing methods after dispose
74+
using var guesser = new Guesser();
75+
guesser.AdjustToCompensateForValue("test");
76+
guesser.Dispose();
77+
78+
Assert.Throws<ObjectDisposedException>(() => guesser.AdjustToCompensateForValue("123"));
79+
}
80+
81+
[Test]
82+
public void Guesser_Parse_WithVariousTypes()
83+
{
84+
// Test the Parse method with different guess types
85+
using var guesser = new Guesser();
86+
87+
// Test int parsing
88+
guesser.AdjustToCompensateForValue(42);
89+
var intResult = guesser.Parse("123");
90+
Assert.That(intResult, Is.EqualTo(123));
91+
92+
// Reset for next test
93+
guesser.Dispose();
94+
using var newGuesser = new Guesser();
95+
96+
// Test decimal parsing
97+
newGuesser.AdjustToCompensateForValue(12.34m);
98+
var decimalResult = newGuesser.Parse("56.78");
99+
Assert.That(decimalResult, Is.EqualTo(56.78m));
100+
}
101+
102+
[Test]
103+
public void Guesser_ShouldDowngradeColumnType_Logic()
104+
{
105+
// Test the ShouldDowngradeColumnTypeToMatchCurrentEstimate method
106+
using var guesser = new Guesser();
107+
guesser.AdjustToCompensateForValue(42); // int type
108+
109+
using var table = new DataTable();
110+
var stringColumn = table.Columns.Add("StringCol", typeof(string));
111+
var objectColumn = table.Columns.Add("ObjectCol", typeof(object));
112+
var intColumn = table.Columns.Add("IntCol", typeof(int));
113+
114+
// Should downgrade string and object columns to int
115+
Assert.That(guesser.ShouldDowngradeColumnTypeToMatchCurrentEstimate(stringColumn), Is.True);
116+
Assert.That(guesser.ShouldDowngradeColumnTypeToMatchCurrentEstimate(objectColumn), Is.True);
117+
118+
// Should not change explicitly typed int column
119+
Assert.That(guesser.ShouldDowngradeColumnTypeToMatchCurrentEstimate(intColumn), Is.False);
120+
}
121+
122+
[Test]
123+
public void Guesser_ExtraLengthPerNonAsciiCharacter_Property()
124+
{
125+
// Test the ExtraLengthPerNonAsciiCharacter property
126+
const int extraLength = 3;
127+
using var guesser = new Guesser { ExtraLengthPerNonAsciiCharacter = extraLength };
128+
129+
Assert.That(guesser.ExtraLengthPerNonAsciiCharacter, Is.EqualTo(extraLength));
130+
}
131+
132+
[Test]
133+
public void DecimalTypeDecider_ParseErrors_ThrowFormatException()
134+
{
135+
// Test error handling in DecimalTypeDecider
136+
var decider = new TypeGuesser.Deciders.DecimalTypeDecider(CultureInfo.InvariantCulture);
137+
138+
var invalidInputs = new[]
139+
{
140+
"not_a_number",
141+
"12.34.56",
142+
"abc123",
143+
"12e",
144+
"1e+abc"
145+
};
146+
147+
foreach (var input in invalidInputs)
148+
{
149+
Assert.Throws<FormatException>(() => decider.Parse(input.AsSpan()));
150+
}
151+
}
152+
153+
[Test]
154+
public void DateTimeTypeDecider_ParseErrors_ThrowFormatException()
155+
{
156+
// Test error handling in DateTimeTypeDecider
157+
var decider = new TypeGuesser.Deciders.DateTimeTypeDecider(CultureInfo.InvariantCulture);
158+
159+
var invalidInputs = new[]
160+
{
161+
"not_a_date",
162+
"32/01/2023",
163+
"13/13/2023",
164+
"2023-99-99",
165+
"invalid_date_format"
166+
};
167+
168+
foreach (var input in invalidInputs)
169+
{
170+
Assert.Throws<FormatException>(() => decider.Parse(input.AsSpan()));
171+
}
172+
}
173+
174+
[Test]
175+
public void BoolTypeDecider_ParseErrors_ThrowFormatException()
176+
{
177+
// Test error handling in BoolTypeDecider
178+
var decider = new TypeGuesser.Deciders.BoolTypeDecider(CultureInfo.InvariantCulture);
179+
180+
var invalidInputs = new[]
181+
{
182+
"maybe",
183+
"unknown",
184+
"2",
185+
"onoff",
186+
"yesno"
187+
};
188+
189+
foreach (var input in invalidInputs)
190+
{
191+
Assert.Throws<FormatException>(() => decider.Parse(input.AsSpan()));
192+
}
193+
}
194+
195+
[Test]
196+
public void IntTypeDecider_ParseErrors_ThrowFormatException()
197+
{
198+
// Test error handling in IntTypeDecider
199+
var decider = new TypeGuesser.Deciders.IntTypeDecider(CultureInfo.InvariantCulture);
200+
201+
var invalidInputs = new[]
202+
{
203+
"12.34",
204+
"not_a_number",
205+
"999999999999999999999999999999",
206+
"1e100"
207+
};
208+
209+
foreach (var input in invalidInputs)
210+
{
211+
Assert.Throws<FormatException>(() => decider.Parse(input.AsSpan()));
212+
}
213+
}
214+
215+
[Test]
216+
public void MixedTyping_VariousScenarios_ThrowMixedTypingException()
217+
{
218+
// Test various mixed typing scenarios
219+
var scenarios = new object[]
220+
{
221+
"string", 42,
222+
true, "test",
223+
12.34m, false,
224+
DateTime.Now, "date"
225+
};
226+
227+
for (int i = 0; i < scenarios.Length; i += 2)
228+
{
229+
using var guesser = new Guesser();
230+
guesser.AdjustToCompensateForValue(scenarios[i]);
231+
232+
Assert.Throws<MixedTypingException>(() =>
233+
guesser.AdjustToCompensateForValue(scenarios[i + 1]));
234+
}
235+
}
236+
237+
[Test]
238+
public void TypeDeciderFactory_UnsupportedTypes_ThrowException()
239+
{
240+
// Test TypeDeciderFactory with unsupported types
241+
var factory = new TypeDeciderFactory(CultureInfo.InvariantCulture);
242+
var unsupportedTypes = new[]
243+
{
244+
typeof(AdditionalCoverageTests),
245+
typeof(Exception),
246+
typeof(AdditionalCoverageTests),
247+
typeof(Action),
248+
typeof(Func<int>)
249+
};
250+
251+
foreach (var type in unsupportedTypes)
252+
{
253+
Assert.Catch<Exception>(() => factory.Create(type));
254+
}
255+
}
256+
257+
[Test]
258+
public void Constructor_WithDatabaseTypeRequest_HandlesVariousTypes()
259+
{
260+
// Test Guesser constructor with different DatabaseTypeRequest types
261+
var requests = new[]
262+
{
263+
new DatabaseTypeRequest(typeof(int)),
264+
new DatabaseTypeRequest(typeof(decimal)),
265+
new DatabaseTypeRequest(typeof(string)),
266+
new DatabaseTypeRequest(typeof(DateTime))
267+
};
268+
269+
foreach (var request in requests)
270+
{
271+
using var guesser = new Guesser(request);
272+
Assert.That(guesser.Guess.CSharpType, Is.EqualTo(request.CSharpType));
273+
}
274+
}
275+
}
276+
}

0 commit comments

Comments
 (0)