11import { complete } from '../../src/complete'
22
3- const SIMPLE_SCHEMA = {
3+ const simpleSchema = {
44 tables : [
55 {
66 catalog : null ,
@@ -24,24 +24,102 @@ const SIMPLE_SCHEMA = {
2424 ] ,
2525}
2626
27+ interface MockTableData {
28+ catalog : string | null
29+ columns : { columnName : string ; description : string } [ ]
30+ database : string | null
31+ tableName : string
32+ }
33+
34+ function mockSchema ( ...tableDefinitions : MockTableData [ ] ) {
35+ return {
36+ functions : [ ] ,
37+ tables : tableDefinitions ,
38+ }
39+ }
40+
41+ function table ( name : string ) : MockTableData {
42+ const demoTables : Record < string , string [ ] > = {
43+ actor : [ 'actor_id' , 'first_name' , 'last_name' , 'last_update' ] ,
44+ actor_info : [ 'actor_id' , 'first_name' , 'last_name' , 'film_info' ] ,
45+ address : [
46+ 'address_id' ,
47+ 'address' ,
48+ 'address2' ,
49+ 'district' ,
50+ 'city_id' ,
51+ 'postal_code' ,
52+ 'phone' ,
53+ 'last_update' ,
54+ ] ,
55+ customer : [
56+ 'customer_id' ,
57+ 'store_id' ,
58+ 'first_name' ,
59+ 'last_name' ,
60+ 'email' ,
61+ 'address_id' ,
62+ 'activebool' ,
63+ 'create_date' ,
64+ 'last_update' ,
65+ 'active' ,
66+ ] ,
67+ film : [
68+ 'film_id' ,
69+ 'title' ,
70+ 'description' ,
71+ 'release_year' ,
72+ 'language_id' ,
73+ 'original_language_id' ,
74+ 'rental_duration' ,
75+ 'rental_rate' ,
76+ 'length' ,
77+ 'replacement_cost' ,
78+ 'rating' ,
79+ 'last_update' ,
80+ 'special_features' ,
81+ 'fulltext' ,
82+ ] ,
83+ notes : [ 'id' , 'note' , 'last_modified' ] ,
84+ staff : [
85+ 'staff_id' ,
86+ 'first_name' ,
87+ 'last_name' ,
88+ 'address_id' ,
89+ 'email' ,
90+ 'store_id' ,
91+ 'active' ,
92+ 'username' ,
93+ 'password' ,
94+ 'last_update' ,
95+ 'picture' ,
96+ ] ,
97+ users : [ 'id' , 'name' , 'email' , 'password' ] ,
98+ }
99+
100+ return {
101+ catalog : null ,
102+ columns : ( demoTables [ name ] || [ ] ) . map ( ( columnName ) => ( {
103+ columnName,
104+ description : '' ,
105+ } ) ) ,
106+ database : 'demo' ,
107+ tableName : name ,
108+ }
109+ }
110+
27111describe ( 'TableName completion' , ( ) => {
28112 test ( 'complete function keyword' , ( ) => {
29- const result = complete (
30- 'SELECT arr' ,
31- { line : 0 , column : 10 } ,
32- SIMPLE_SCHEMA
33- )
113+ const result = complete ( 'SELECT arr' , { line : 0 , column : 10 } , simpleSchema )
114+
34115 expect ( result . candidates . length ) . toEqual ( 2 )
35116 expect ( result . candidates [ 0 ] . label ) . toEqual ( 'array_concat()' )
36117 expect ( result . candidates [ 1 ] . label ) . toEqual ( 'array_contains()' )
37118 } )
38119
39120 test ( 'complete function keyword' , ( ) => {
40- const result = complete (
41- 'SELECT ARR' ,
42- { line : 0 , column : 10 } ,
43- SIMPLE_SCHEMA
44- )
121+ const result = complete ( 'SELECT ARR' , { line : 0 , column : 10 } , simpleSchema )
122+
45123 expect ( result . candidates . length ) . toEqual ( 2 )
46124 expect ( result . candidates [ 0 ] . label ) . toEqual ( 'ARRAY_CONCAT()' )
47125 expect ( result . candidates [ 1 ] . label ) . toEqual ( 'ARRAY_CONTAINS()' )
@@ -51,8 +129,9 @@ describe('TableName completion', () => {
51129 const result = complete (
52130 'SELECT T FROM TABLE1' ,
53131 { line : 0 , column : 8 } ,
54- SIMPLE_SCHEMA
132+ simpleSchema
55133 )
134+
56135 expect ( result . candidates . length ) . toEqual ( 1 )
57136 expect ( result . candidates [ 0 ] . label ) . toEqual ( 'TABLE1' )
58137 } )
@@ -61,8 +140,9 @@ describe('TableName completion', () => {
61140 const result = complete (
62141 'SELECT ta FROM TABLE1 as tab' ,
63142 { line : 0 , column : 9 } ,
64- SIMPLE_SCHEMA
143+ simpleSchema
65144 )
145+
66146 expect ( result . candidates . length ) . toEqual ( 3 )
67147 expect ( result . candidates ) . toEqual (
68148 expect . arrayContaining ( [ expect . objectContaining ( { label : 'tab' } ) ] )
@@ -72,8 +152,9 @@ describe('TableName completion', () => {
72152 const result = complete (
73153 'SELECT FROM TABLE1' ,
74154 { line : 0 , column : 6 } ,
75- SIMPLE_SCHEMA
155+ simpleSchema
76156 )
157+
77158 expect ( result . candidates . length ) . toEqual ( 2 )
78159 expect ( result . candidates [ 0 ] . label ) . toEqual ( 'Select all columns from TABLE1' )
79160 expect ( result . candidates [ 0 ] . insertText ) . toEqual (
@@ -88,8 +169,9 @@ describe('TableName completion', () => {
88169 const result = complete (
89170 'SELEC FROM TABLE1' ,
90171 { line : 0 , column : 5 } ,
91- SIMPLE_SCHEMA
172+ simpleSchema
92173 )
174+
93175 expect ( result . candidates . length ) . toEqual ( 2 )
94176 expect ( result . candidates [ 0 ] . label ) . toEqual ( 'SELECT' )
95177 expect ( result . candidates [ 1 ] . label ) . toEqual ( 'Select all columns from TABLE1' )
@@ -102,7 +184,7 @@ describe('TableName completion', () => {
102184 const result = complete (
103185 'SELECT FROM TABLE1' ,
104186 { line : 0 , column : 7 } ,
105- SIMPLE_SCHEMA
187+ simpleSchema
106188 )
107189 const expected = [
108190 expect . objectContaining ( {
@@ -114,87 +196,46 @@ describe('TableName completion', () => {
114196 } )
115197
116198 test ( 'complete table name after FROM keyword with partial input' , ( ) => {
117- const schema = {
118- tables : [
119- {
120- catalog : null ,
121- database : null ,
122- tableName : 'notes' ,
123- columns : [ { columnName : 'id' , description : '' } ] ,
124- } ,
125- {
126- catalog : null ,
127- database : null ,
128- tableName : 'users' ,
129- columns : [ { columnName : 'name' , description : '' } ] ,
130- } ,
131- ] ,
132- functions : [ ] ,
133- }
199+ const schema = mockSchema ( table ( 'notes' ) , table ( 'users' ) )
134200
135201 const result = complete (
136202 'SELECT * FROM not' ,
137203 { line : 0 , column : 17 } ,
138204 schema
139205 )
140206
141- // Should suggest table names
142207 const labels = result . candidates . map ( ( c ) => c . label )
208+
143209 expect ( labels ) . toContain ( 'notes' )
144210
145- // Should NOT include any column suggestions (even if qualified)
146211 expect ( labels ) . not . toEqual ( expect . arrayContaining ( [ 'id' , 'name' ] ) )
147212
148- // All candidates should be tables (CompletionItemKind.Constant = 21)
149- const TABLE_KIND = 21
150- expect ( result . candidates . every ( ( c ) => c . kind === TABLE_KIND ) ) . toBe ( true )
213+ expect ( result . candidates . every ( ( c ) => c . kind === 21 ) ) . toBe ( true )
151214 } )
152215
153216 test ( 'complete table name with database-qualified tables' , ( ) => {
154- const schema = {
155- tables : [
156- {
157- catalog : null ,
158- database : 'squeal' ,
159- tableName : 'actor' ,
160- columns : [ { columnName : 'actor_id' , description : '' } ] ,
161- } ,
162- {
163- catalog : null ,
164- database : 'squeal' ,
165- tableName : 'actor_info' ,
166- columns : [ { columnName : 'actor_id' , description : '' } ] ,
167- } ,
168- {
169- catalog : null ,
170- database : 'squeal' ,
171- tableName : 'film' ,
172- columns : [ { columnName : 'film_id' , description : '' } ] ,
173- } ,
174- ] ,
175- functions : [ ] ,
176- }
217+ const schema = mockSchema (
218+ table ( 'actor' ) ,
219+ table ( 'actor_info' ) ,
220+ table ( 'film' )
221+ )
177222
178223 // Test: typing 'a' after FROM should match 'actor' and 'actor_info'
179224 const result = complete ( 'SELECT * FROM a' , { line : 0 , column : 15 } , schema )
180225
181226 const labels = result . candidates . map ( ( c ) => c . label )
182- expect ( labels ) . toContain ( 'actor' )
183- expect ( labels ) . toContain ( 'actor_info' )
184- expect ( labels ) . not . toContain ( 'film' )
227+
228+ expect ( labels ) . toEqual ( [ 'actor' , 'actor_info' ] )
185229 } )
186230
187231 test ( 'complete table name when SQL parses successfully' , ( ) => {
188232 // This tests the case when the parser treats partial table name as valid
189233 // See https://github.com/deepnote/sql-language-server/issues/24
190- const schema = {
191- tables : [
192- { catalog : null , database : null , tableName : 'actor' , columns : [ ] } ,
193- { catalog : null , database : null , tableName : 'actor_info' , columns : [ ] } ,
194- { catalog : null , database : null , tableName : 'film' , columns : [ ] } ,
195- ] ,
196- functions : [ ] ,
197- }
234+ const schema = mockSchema (
235+ table ( 'actor' ) ,
236+ table ( 'actor_info' ) ,
237+ table ( 'film' )
238+ )
198239
199240 const result = complete (
200241 'SELECT * FROM act' ,
@@ -203,47 +244,18 @@ describe('TableName completion', () => {
203244 )
204245
205246 const labels = result . candidates . map ( ( c ) => c . label )
206- expect ( labels ) . toContain ( 'actor' )
207- expect ( labels ) . toContain ( 'actor_info' )
208- expect ( labels ) . not . toContain ( 'film' )
247+
248+ expect ( labels ) . toEqual ( [ 'actor' , 'actor_info' ] )
209249 } )
210250
211251 test ( 'complete table name with partial input after typing more characters' , ( ) => {
212- const schema = {
213- tables : [
214- {
215- catalog : null ,
216- database : 'squeal' ,
217- tableName : 'actor' ,
218- columns : [ ] ,
219- } ,
220- {
221- catalog : null ,
222- database : 'squeal' ,
223- tableName : 'actor_info' ,
224- columns : [ ] ,
225- } ,
226- {
227- catalog : null ,
228- database : 'squeal' ,
229- tableName : 'film' ,
230- columns : [ ] ,
231- } ,
232- {
233- catalog : null ,
234- database : 'squeal' ,
235- tableName : 'film_actor' ,
236- columns : [ ] ,
237- } ,
238- {
239- catalog : null ,
240- database : 'squeal' ,
241- tableName : 'customer' ,
242- columns : [ ] ,
243- } ,
244- ] ,
245- functions : [ ] ,
246- }
252+ const schema = mockSchema (
253+ table ( 'actor' ) ,
254+ table ( 'actor_info' ) ,
255+ table ( 'customer' ) ,
256+ table ( 'film' ) ,
257+ table ( 'film_actor' )
258+ )
247259
248260 // Test: typing 'fil' should match 'film' and 'film_actor'
249261 const result = complete (
@@ -253,9 +265,7 @@ describe('TableName completion', () => {
253265 )
254266
255267 const labels = result . candidates . map ( ( c ) => c . label )
256- expect ( labels ) . toContain ( 'film' )
257- expect ( labels ) . toContain ( 'film_actor' )
258- expect ( labels ) . not . toContain ( 'actor' )
259- expect ( labels ) . not . toContain ( 'customer' )
268+
269+ expect ( labels ) . toEqual ( [ 'film' , 'film_actor' ] )
260270 } )
261271} )
0 commit comments