@@ -7,8 +7,10 @@ import (
77 "testing"
88
99 "github.com/shivasurya/code-pathfinder/sast-engine/dsl"
10+ "github.com/shivasurya/code-pathfinder/sast-engine/graph"
1011 "github.com/shivasurya/code-pathfinder/sast-engine/graph/callgraph/core"
1112 "github.com/stretchr/testify/assert"
13+ "github.com/stretchr/testify/require"
1214)
1315
1416// Helper function to create test rules (duplicated from ci_test.go).
@@ -171,3 +173,173 @@ func TestPrintDetections(t *testing.T) {
171173 assert .Contains (t , output , "Confidence: 70%" )
172174 })
173175}
176+
177+ func TestExtractContainerFiles (t * testing.T ) {
178+ t .Run ("extracts Dockerfile and docker-compose files" , func (t * testing.T ) {
179+ cg := & graph.CodeGraph {
180+ Nodes : map [string ]* graph.Node {
181+ "node1" : {Type : "dockerfile_instruction" , File : "/path/to/Dockerfile" },
182+ "node2" : {Type : "dockerfile_instruction" , File : "/path/to/Dockerfile.dev" },
183+ "node3" : {Type : "compose_service" , File : "/path/to/docker-compose.yml" },
184+ "node4" : {Type : "method_declaration" , File : "/path/to/main.go" },
185+ },
186+ }
187+
188+ dockerFiles , composeFiles := extractContainerFiles (cg )
189+
190+ assert .Equal (t , 2 , len (dockerFiles ))
191+ assert .Contains (t , dockerFiles , "/path/to/Dockerfile" )
192+ assert .Contains (t , dockerFiles , "/path/to/Dockerfile.dev" )
193+
194+ assert .Equal (t , 1 , len (composeFiles ))
195+ assert .Contains (t , composeFiles , "/path/to/docker-compose.yml" )
196+ })
197+
198+ t .Run ("handles duplicates" , func (t * testing.T ) {
199+ cg := & graph.CodeGraph {
200+ Nodes : map [string ]* graph.Node {
201+ "node1" : {Type : "dockerfile_instruction" , File : "/path/to/Dockerfile" },
202+ "node2" : {Type : "dockerfile_instruction" , File : "/path/to/Dockerfile" },
203+ "node3" : {Type : "compose_service" , File : "/path/to/docker-compose.yml" },
204+ "node4" : {Type : "compose_service" , File : "/path/to/docker-compose.yml" },
205+ },
206+ }
207+
208+ dockerFiles , composeFiles := extractContainerFiles (cg )
209+
210+ // Should deduplicate
211+ assert .Equal (t , 1 , len (dockerFiles ))
212+ assert .Equal (t , 1 , len (composeFiles ))
213+ })
214+
215+ t .Run ("returns empty for no container files" , func (t * testing.T ) {
216+ cg := & graph.CodeGraph {
217+ Nodes : map [string ]* graph.Node {
218+ "node1" : {Type : "method_declaration" , File : "/path/to/main.go" },
219+ "node2" : {Type : "class_declaration" , File : "/path/to/app.java" },
220+ },
221+ }
222+
223+ dockerFiles , composeFiles := extractContainerFiles (cg )
224+
225+ assert .Equal (t , 0 , len (dockerFiles ))
226+ assert .Equal (t , 0 , len (composeFiles ))
227+ })
228+ }
229+
230+ func TestSplitLines (t * testing.T ) {
231+ t .Run ("splits simple content" , func (t * testing.T ) {
232+ content := "line 1\n line 2\n line 3"
233+ lines := splitLines (content )
234+
235+ assert .Equal (t , 3 , len (lines ))
236+ assert .Equal (t , "line 1" , lines [0 ])
237+ assert .Equal (t , "line 2" , lines [1 ])
238+ assert .Equal (t , "line 3" , lines [2 ])
239+ })
240+
241+ t .Run ("handles empty lines" , func (t * testing.T ) {
242+ content := "line 1\n \n line 3"
243+ lines := splitLines (content )
244+
245+ assert .Equal (t , 3 , len (lines ))
246+ assert .Equal (t , "line 1" , lines [0 ])
247+ assert .Equal (t , "" , lines [1 ])
248+ assert .Equal (t , "line 3" , lines [2 ])
249+ })
250+
251+ t .Run ("handles Windows line endings" , func (t * testing.T ) {
252+ content := "line 1\r \n line 2\r \n line 3"
253+ lines := splitLines (content )
254+
255+ assert .Equal (t , 3 , len (lines ))
256+ assert .Equal (t , "line 1" , lines [0 ])
257+ assert .Equal (t , "line 2" , lines [1 ])
258+ assert .Equal (t , "line 3" , lines [2 ])
259+ })
260+
261+ t .Run ("handles empty content" , func (t * testing.T ) {
262+ lines := splitLines ("" )
263+ assert .Equal (t , 0 , len (lines ))
264+ })
265+
266+ t .Run ("preserves last line without newline" , func (t * testing.T ) {
267+ content := "line 1\n line 2"
268+ lines := splitLines (content )
269+
270+ assert .Equal (t , 2 , len (lines ))
271+ assert .Equal (t , "line 1" , lines [0 ])
272+ assert .Equal (t , "line 2" , lines [1 ])
273+ })
274+ }
275+
276+ func TestGenerateCodeSnippet (t * testing.T ) {
277+ // Create a temporary test file
278+ content := `line 1
279+ line 2
280+ line 3
281+ line 4
282+ line 5
283+ line 6
284+ line 7`
285+
286+ tmpFile , err := os .CreateTemp ("" , "test-snippet-*.txt" )
287+ require .NoError (t , err )
288+ defer os .Remove (tmpFile .Name ())
289+
290+ _ , err = tmpFile .WriteString (content )
291+ require .NoError (t , err )
292+ tmpFile .Close ()
293+
294+ t .Run ("generates snippet with context" , func (t * testing.T ) {
295+ snippet := generateCodeSnippet (tmpFile .Name (), 4 , 2 )
296+
297+ assert .Equal (t , 5 , len (snippet .Lines ))
298+ assert .Equal (t , 2 , snippet .StartLine )
299+ assert .Equal (t , 4 , snippet .HighlightLine )
300+
301+ // Check line numbers and content
302+ assert .Equal (t , 2 , snippet .Lines [0 ].Number )
303+ assert .Equal (t , "line 2" , snippet .Lines [0 ].Content )
304+ assert .False (t , snippet .Lines [0 ].IsHighlight )
305+
306+ assert .Equal (t , 4 , snippet .Lines [2 ].Number )
307+ assert .Equal (t , "line 4" , snippet .Lines [2 ].Content )
308+ assert .True (t , snippet .Lines [2 ].IsHighlight )
309+
310+ assert .Equal (t , 6 , snippet .Lines [4 ].Number )
311+ assert .Equal (t , "line 6" , snippet .Lines [4 ].Content )
312+ })
313+
314+ t .Run ("handles line at start of file" , func (t * testing.T ) {
315+ snippet := generateCodeSnippet (tmpFile .Name (), 1 , 2 )
316+
317+ assert .Equal (t , 3 , len (snippet .Lines )) // Lines 1, 2, 3
318+ assert .Equal (t , 1 , snippet .StartLine )
319+ assert .Equal (t , 1 , snippet .HighlightLine )
320+ assert .True (t , snippet .Lines [0 ].IsHighlight )
321+ })
322+
323+ t .Run ("handles line at end of file" , func (t * testing.T ) {
324+ snippet := generateCodeSnippet (tmpFile .Name (), 7 , 2 )
325+
326+ assert .Equal (t , 3 , len (snippet .Lines )) // Lines 5, 6, 7
327+ assert .Equal (t , 5 , snippet .StartLine )
328+ assert .Equal (t , 7 , snippet .HighlightLine )
329+ assert .True (t , snippet .Lines [2 ].IsHighlight )
330+ })
331+
332+ t .Run ("handles invalid line number" , func (t * testing.T ) {
333+ snippet := generateCodeSnippet (tmpFile .Name (), 100 , 2 )
334+
335+ assert .Equal (t , 0 , len (snippet .Lines ))
336+ assert .Equal (t , 0 , snippet .StartLine )
337+ assert .Equal (t , 0 , snippet .HighlightLine )
338+ })
339+
340+ t .Run ("handles nonexistent file" , func (t * testing.T ) {
341+ snippet := generateCodeSnippet ("/nonexistent/file.txt" , 1 , 2 )
342+
343+ assert .Equal (t , 0 , len (snippet .Lines ))
344+ })
345+ }
0 commit comments