Skip to content

Commit 804bf14

Browse files
authored
Add VectorizerFactoryAnalyzer and associated tests for property initialization and weights calculation (#266)
1 parent 89f0c65 commit 804bf14

File tree

3 files changed

+551
-0
lines changed

3 files changed

+551
-0
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.Testing;
3+
using Xunit;
4+
using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.AnalyzerVerifier<Weaviate.Client.Analyzers.VectorizerFactoryAnalyzer>;
5+
6+
namespace Weaviate.Client.Analyzers.Tests;
7+
8+
public class VectorizerFactoryAnalyzerTests
9+
{
10+
private const string VectorizerConfigBaseSource =
11+
@"
12+
namespace Weaviate.Client.Models
13+
{
14+
public abstract record VectorizerConfig { }
15+
16+
public record WeightedFields
17+
{
18+
public double[]? Weights { get; set; }
19+
}
20+
}
21+
22+
namespace Weaviate.Client.Models
23+
{
24+
internal static class Vectorizer
25+
{
26+
internal record VectorizerWeights
27+
{
28+
public static VectorizerWeights FromWeightedFields(
29+
WeightedFields? imageFields = null,
30+
WeightedFields? textFields = null,
31+
WeightedFields? videoFields = null
32+
) => new();
33+
}
34+
35+
public record TestVectorizer : VectorizerConfig
36+
{
37+
public string? Model { get; set; }
38+
public string? BaseURL { get; set; }
39+
public WeightedFields? ImageFields { get; set; }
40+
public WeightedFields? TextFields { get; set; }
41+
public WeightedFields? VideoFields { get; set; }
42+
internal VectorizerWeights? Weights { get; set; }
43+
}
44+
}
45+
}";
46+
47+
[Fact]
48+
public async Task MissingPropertyInitialization_ReportsDiagnostic()
49+
{
50+
var testCode =
51+
VectorizerConfigBaseSource
52+
+ @"
53+
namespace Weaviate.Client
54+
{
55+
using Weaviate.Client.Models;
56+
57+
public class VectorizerFactory
58+
{
59+
public VectorizerConfig {|#0:TestVectorizer|}(
60+
string? model = null,
61+
string? baseURL = null
62+
) =>
63+
new Models.Vectorizer.TestVectorizer
64+
{
65+
Model = model
66+
// Missing: BaseURL = baseURL
67+
};
68+
}
69+
}";
70+
71+
var expected = VerifyCS
72+
.Diagnostic(VectorizerFactoryAnalyzer.MissingPropertyDiagnosticId)
73+
.WithLocation(0)
74+
.WithArguments("TestVectorizer", "BaseURL");
75+
76+
await VerifyCS.VerifyAnalyzerAsync(testCode, expected);
77+
}
78+
79+
[Fact]
80+
public async Task AllPropertiesInitialized_NoDiagnostic()
81+
{
82+
var testCode =
83+
VectorizerConfigBaseSource
84+
+ @"
85+
namespace Weaviate.Client
86+
{
87+
using Weaviate.Client.Models;
88+
89+
public class VectorizerFactory
90+
{
91+
public VectorizerConfig TestVectorizer(
92+
string? model = null,
93+
string? baseURL = null
94+
) =>
95+
new Models.Vectorizer.TestVectorizer
96+
{
97+
Model = model,
98+
BaseURL = baseURL
99+
};
100+
}
101+
}";
102+
103+
await VerifyCS.VerifyAnalyzerAsync(testCode);
104+
}
105+
106+
[Fact]
107+
public async Task MissingWeightedFieldInWeightsCalculation_ReportsDiagnostic()
108+
{
109+
var testCode =
110+
VectorizerConfigBaseSource
111+
+ @"
112+
namespace Weaviate.Client
113+
{
114+
using Weaviate.Client.Models;
115+
using static Weaviate.Client.Models.Vectorizer;
116+
117+
public class VectorizerFactory
118+
{
119+
public VectorizerConfig TestVectorizer(
120+
WeightedFields imageFields,
121+
WeightedFields textFields,
122+
WeightedFields videoFields
123+
) =>
124+
new Models.Vectorizer.TestVectorizer
125+
{
126+
ImageFields = imageFields,
127+
TextFields = textFields,
128+
VideoFields = videoFields,
129+
{|#0:Weights = VectorizerWeights.FromWeightedFields(imageFields, textFields)|}
130+
};
131+
}
132+
}";
133+
134+
var expected = VerifyCS
135+
.Diagnostic(VectorizerFactoryAnalyzer.MissingWeightFieldDiagnosticId)
136+
.WithLocation(0)
137+
.WithArguments("TestVectorizer", "videoFields");
138+
139+
await VerifyCS.VerifyAnalyzerAsync(testCode, expected);
140+
}
141+
142+
[Fact]
143+
public async Task AllWeightedFieldsInWeightsCalculation_NoDiagnostic()
144+
{
145+
var testCode =
146+
VectorizerConfigBaseSource
147+
+ @"
148+
namespace Weaviate.Client
149+
{
150+
using Weaviate.Client.Models;
151+
using static Weaviate.Client.Models.Vectorizer;
152+
153+
public class VectorizerFactory
154+
{
155+
public VectorizerConfig TestVectorizer(
156+
WeightedFields imageFields,
157+
WeightedFields textFields,
158+
WeightedFields videoFields
159+
) =>
160+
new Models.Vectorizer.TestVectorizer
161+
{
162+
ImageFields = imageFields,
163+
TextFields = textFields,
164+
VideoFields = videoFields,
165+
Weights = VectorizerWeights.FromWeightedFields(imageFields, textFields, videoFields)
166+
};
167+
}
168+
}";
169+
170+
await VerifyCS.VerifyAnalyzerAsync(testCode);
171+
}
172+
173+
[Fact]
174+
public async Task NamedArgumentsInWeightsCalculation_NoDiagnostic()
175+
{
176+
var testCode =
177+
VectorizerConfigBaseSource
178+
+ @"
179+
namespace Weaviate.Client
180+
{
181+
using Weaviate.Client.Models;
182+
using static Weaviate.Client.Models.Vectorizer;
183+
184+
public class VectorizerFactory
185+
{
186+
public VectorizerConfig TestVectorizer(
187+
WeightedFields imageFields,
188+
WeightedFields textFields,
189+
WeightedFields videoFields
190+
) =>
191+
new Models.Vectorizer.TestVectorizer
192+
{
193+
ImageFields = imageFields,
194+
TextFields = textFields,
195+
VideoFields = videoFields,
196+
Weights = VectorizerWeights.FromWeightedFields(imageFields, textFields, videoFields: videoFields)
197+
};
198+
}
199+
}";
200+
201+
await VerifyCS.VerifyAnalyzerAsync(testCode);
202+
}
203+
204+
[Fact]
205+
public async Task NoWeightedFieldsParameters_NoDiagnostic()
206+
{
207+
var testCode =
208+
VectorizerConfigBaseSource
209+
+ @"
210+
namespace Weaviate.Client
211+
{
212+
using Weaviate.Client.Models;
213+
214+
public class VectorizerFactory
215+
{
216+
public VectorizerConfig TestVectorizer(
217+
string? model = null
218+
) =>
219+
new Models.Vectorizer.TestVectorizer
220+
{
221+
Model = model
222+
};
223+
}
224+
}";
225+
226+
await VerifyCS.VerifyAnalyzerAsync(testCode);
227+
}
228+
229+
[Fact]
230+
public async Task VectorizerFactoryMultiClass_AlsoAnalyzed()
231+
{
232+
var testCode =
233+
VectorizerConfigBaseSource
234+
+ @"
235+
namespace Weaviate.Client
236+
{
237+
using Weaviate.Client.Models;
238+
239+
public class VectorizerFactoryMulti
240+
{
241+
public VectorizerConfig {|#0:TestVectorizer|}(
242+
string? model = null,
243+
string? baseURL = null
244+
) =>
245+
new Models.Vectorizer.TestVectorizer
246+
{
247+
Model = model
248+
// Missing: BaseURL = baseURL
249+
};
250+
}
251+
}";
252+
253+
var expected = VerifyCS
254+
.Diagnostic(VectorizerFactoryAnalyzer.MissingPropertyDiagnosticId)
255+
.WithLocation(0)
256+
.WithArguments("TestVectorizer", "BaseURL");
257+
258+
await VerifyCS.VerifyAnalyzerAsync(testCode, expected);
259+
}
260+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11

22
### New Rules
3+
4+
Rule ID | Category | Severity | Notes
5+
--------|----------|----------|-------
6+
WEAVIATE001 | Usage | Error | AutoArrayUsageAnalyzer
7+
WEAVIATE002 | Usage | Warning | VectorizerFactoryAnalyzer - Missing property initialization
8+
WEAVIATE003 | Usage | Warning | VectorizerFactoryAnalyzer - Missing field in Weights calculation

0 commit comments

Comments
 (0)