1- namespace DotPrompt . Sql . Test ;
2-
31using System ;
42using System . Collections . Generic ;
5- using System . IO ;
3+ using System . Data ;
4+ using System . Data . SQLite ;
5+ using System . Reflection ;
6+ using Microsoft . Data . Sqlite ;
7+ using System . Threading . Tasks ;
8+ using Dapper ;
9+ using DotPrompt . Sql ;
10+ using Microsoft . Data . SqlClient ;
11+ using Testcontainers . MsSql ;
612using Xunit ;
713
8- public class SqlPromptEntityTests : IDisposable
14+ public class SqlPromptRepositoryTests : IAsyncLifetime
915{
10- private readonly List < string > _testFiles = new ( ) ;
16+ private readonly MsSqlContainer _sqlServerContainer ;
17+ private IDbConnection _connection ;
18+ private SqlPromptRepository _repository ;
1119
12- // Helper method to create and track test YAML files
13- private void CreateTestYamlFile ( string filePath , string content )
20+ public SqlPromptRepositoryTests ( )
1421 {
15- File . WriteAllText ( filePath , content ) ;
16- _testFiles . Add ( filePath ) ; // Track the file for later cleanup
22+ _sqlServerContainer = new MsSqlBuilder ( )
23+ . WithImage ( "mcr.microsoft.com/mssql/server:2022-latest" )
24+ . WithPassword ( "YourStrong(!)Password" )
25+ . Build ( ) ;
1726 }
1827
19- [ Fact ]
20- public void FromPromptFile_ValidYaml_ReturnsSqlPromptEntity ( )
28+ public async Task InitializeAsync ( )
2129 {
22- // Arrange
23- string filePath = "test_prompt.yaml" ;
24- string yamlContent = @"
25- model: gpt-4
26- config:
27- name: TestPrompt
28- outputFormat: json
29- maxTokens: 200
30- input:
31- parameters:
32- param1: value1
33- param2: value2
34- default:
35- param1: default1
36- param2: default2
37- prompts:
38- system: System message
39- user: User message" ;
40-
41- CreateTestYamlFile ( filePath , yamlContent ) ;
30+ await _sqlServerContainer . StartAsync ( ) ;
4231
43- // Act
44- var result = SqlPromptEntity . FromPromptFile ( filePath ) ;
32+ _connection = new SqlConnection ( _sqlServerContainer . GetConnectionString ( ) ) ;
33+ _connection . Open ( ) ;
4534
46- // Assert
47- Assert . NotNull ( result ) ;
48- Assert . Equal ( "gpt-4" , result . Model ) ;
49- Assert . Equal ( "TestPrompt" , result . PromptName ) ;
50- Assert . Equal ( "json" , result . OutputFormat ) ;
51- Assert . Equal ( 200 , result . MaxTokens ) ;
52- Assert . Equal ( "System message" , result . SystemPrompt ) ;
53- Assert . Equal ( "User message" , result . UserPrompt ) ;
54- Assert . Equal ( "value1" , result . Parameters [ "param1" ] ) ;
55- Assert . Equal ( "default1" , result . Default [ "param1" ] ) ;
35+ _repository = new SqlPromptRepository ( _connection ) ;
36+ await InitializeDatabase ( ) ;
5637 }
5738
58- [ Fact ]
59- public void FromPromptFile_FileDoesNotExist_ThrowsFileNotFoundException ( )
39+ public Task DisposeAsync ( )
6040 {
61- // Arrange
62- string invalidPath = "non_existent.yaml" ;
41+ _sqlServerContainer . StopAsync ( ) ;
42+ _connection ? . Dispose ( ) ;
43+ return Task . CompletedTask ;
44+ }
45+
46+ private static string LoadSql ( string resourceName )
47+ {
48+ // Get the assembly containing the embedded SQL files
49+ var assembly = Assembly . Load ( "DotPrompt.Sql" ) ; // Name of the referenced assembly
50+
51+ // Find the full resource name (includes namespace path)
52+ string ? fullResourceName = assembly . GetManifestResourceNames ( )
53+ . FirstOrDefault ( name => name . EndsWith ( resourceName , StringComparison . OrdinalIgnoreCase ) ) ;
6354
64- // Act & Assert
65- var exception = Assert . Throws < FileNotFoundException > ( ( ) => SqlPromptEntity . FromPromptFile ( invalidPath ) ) ;
66- Assert . Contains ( "The specified file was not found" , exception . Message ) ;
55+ if ( fullResourceName == null )
56+ {
57+ throw new FileNotFoundException ( $ "Resource { resourceName } not found in assembly { assembly . FullName } ") ;
58+ }
59+
60+ // Read the embedded resource stream
61+ using var stream = assembly . GetManifestResourceStream ( fullResourceName ) ;
62+ using var reader = new StreamReader ( stream ) ;
63+ return reader . ReadToEnd ( ) ;
64+ }
65+ private async Task InitializeDatabase ( )
66+ {
67+ string tables = LoadSql ( "CreateDefaultPromptTables.sql" ) ;
68+ await _connection . ExecuteAsync ( tables ) ;
69+
70+ string procs = LoadSql ( "AddSqlPrompt.sql" ) ;
71+ await _connection . ExecuteAsync ( procs ) ;
6772 }
6873
6974 [ Fact ]
70- public void FromPromptFile_MissingMandatoryFields_ThrowsException ( )
75+ public async Task AddSqlPrompt_ValidPrompt_InsertsSuccessfully ( )
7176 {
7277 // Arrange
73- string filePath = "test_missing_optional.yaml" ;
74- string yamlContent = @"
75- config:
76- name: TestPrompt
77- outputFormat: json
78- maxTokens: 100
79- prompts:
80- system: Default system prompt
81- user: Default user prompt" ;
82-
83- CreateTestYamlFile ( filePath , yamlContent ) ;
84-
85- // Act & Assert
86- Assert . Throws < ApplicationException > ( ( ) => SqlPromptEntity . FromPromptFile ( filePath ) ) ;
78+ var entity = new SqlPromptEntity
79+ {
80+ PromptName = "myprompt" ,
81+ Model = "gpt4" ,
82+ OutputFormat = "json" ,
83+ MaxTokens = 500 ,
84+ SystemPrompt = "Optimize SQL queries." ,
85+ UserPrompt = "Suggest indexing improvements." ,
86+ Parameters = new Dictionary < string , string >
87+ {
88+ { "Temperature" , "0.7" } ,
89+ { "TopP" , "0.9" }
90+ } ,
91+ Default = new Dictionary < string , object >
92+ {
93+ { "Temperature" , "0.5" }
94+ }
95+ } ;
96+
97+ // Act
98+ bool result = await _repository . AddSqlPrompt ( entity ) ;
99+
100+ // Assert
101+ Assert . True ( result , "Expected new prompt version to be inserted." ) ;
87102 }
88103
89104 [ Fact ]
90- public void FromPromptFile_InvalidDataType_ThrowsException ( )
105+ public async Task AddSqlPrompt_SamePromptNoChanges_DoesNotInsertNewVersion ( )
91106 {
92107 // Arrange
93- string filePath = "test_invalid_type.yaml" ;
94- string yamlContent = @"
95- model: gpt-4
96- config:
97- name: TestPrompt
98- outputFormat: json
99- maxTokens: not_a_number
100- prompts:
101- system: System prompt
102- user: User prompt" ;
103-
104- CreateTestYamlFile ( filePath , yamlContent ) ;
105-
106- // Act & Assert
107- Assert . Throws < FormatException > ( ( ) => SqlPromptEntity . FromPromptFile ( filePath ) ) ;
108+ var entity = new SqlPromptEntity
109+ {
110+ PromptName = "myprompt" ,
111+ Model = "gpt4" ,
112+ OutputFormat = "json" ,
113+ MaxTokens = 200 ,
114+ SystemPrompt = "Optimize SQL queries." ,
115+ UserPrompt = "Suggest indexing improvements." ,
116+ Parameters = new Dictionary < string , string >
117+ {
118+ { "Temperature" , "0.7" } ,
119+ { "TopP" , "0.9" }
120+ } ,
121+ Default = new Dictionary < string , object >
122+ {
123+ { "Temperature" , "0.5" }
124+ }
125+ } ;
126+
127+ await _repository . AddSqlPrompt ( entity ) ; // First insert
128+
129+ // Act
130+ bool result = await _repository . AddSqlPrompt ( entity ) ; // Try inserting again with no changes
131+
132+ // Assert
133+ Assert . False ( result , "No new version should be inserted when nothing has changed." ) ;
108134 }
109135
110- // Cleanup method called after each test
111- public void Dispose ( )
136+ [ Fact ]
137+ public async Task AddSqlPrompt_WhenMaxTokensChanges_ShouldInsertNewVersion ( )
112138 {
113- foreach ( var file in _testFiles )
139+ // Arrange
140+ var entity1 = new SqlPromptEntity
114141 {
115- if ( File . Exists ( file ) )
116- {
117- File . Delete ( file ) ;
118- }
119- }
142+ PromptName = "noprompt" ,
143+ Model = "gpt4" ,
144+ OutputFormat = "json" ,
145+ MaxTokens = 500 ,
146+ SystemPrompt = "Optimize SQL queries." ,
147+ UserPrompt = "Suggest indexing improvements." ,
148+ Parameters = new Dictionary < string , string > { { "Temperature" , "0.7" } } ,
149+ Default = new Dictionary < string , object > { { "Temperature" , "0.5" } }
150+ } ;
151+
152+ var entity2 = new SqlPromptEntity
153+ {
154+ PromptName = "noprompt" , // Same prompt name
155+ Model = "gpt4" ,
156+ OutputFormat = "json" ,
157+ MaxTokens = 512 , // Changed value
158+ SystemPrompt = "Optimize SQL queries." ,
159+ UserPrompt = "Suggest indexing improvements." ,
160+ Parameters = new Dictionary < string , string > { { "Temperature" , "0.7" } } ,
161+ Default = new Dictionary < string , object > { { "Temperature" , "0.5" } }
162+ } ;
163+
164+ await _repository . AddSqlPrompt ( entity1 ) ; // Insert first version
165+
166+ // Act
167+ bool result = await _repository . AddSqlPrompt ( entity2 ) ;
168+
169+ // Assert
170+ Assert . True ( result , "A new version should be inserted when MaxTokens changes." ) ;
120171 }
121- }
172+ }
0 commit comments