Skip to content

Commit af0ced0

Browse files
committed
clean up, tests
1 parent c9a81cb commit af0ced0

11 files changed

Lines changed: 480 additions & 83 deletions

File tree

cmd/cryptoanalysis/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ func run() error {
4343
if err != nil {
4444
return err
4545
}
46-
fmt.Printf("Docker Compose file written to %s\n", composeFilePath)
4746

47+
if cfg.Verbose {
48+
fmt.Printf("Docker Compose file written to %s\n", composeFilePath)
49+
}
50+
4851
// Run Docker Compose
4952
if err := composer.RunCompose(composeFilePath, cfg.Timeout); err != nil {
5053
return fmt.Errorf("failed to run Docker Compose: %w", err)

internal/compose/composer.go

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,6 @@ import (
1010
"github.com/ASSERT-KTH/go-cryptoapi/internal/dataset"
1111
)
1212

13-
// ComposerConfig holds configuration for the composer
14-
type ComposerConfig struct {
15-
OutDir string
16-
Parallelism int
17-
}
18-
19-
// DefaultComposerConfig returns a default composer configuration
20-
func DefaultComposerConfig() *ComposerConfig {
21-
return &ComposerConfig{
22-
OutDir: "data/analysis",
23-
Parallelism: 4,
24-
}
25-
}
26-
2713
// Composer interface defines methods for generating Docker Compose configurations
2814
type Composer interface {
2915
// ComposeStr returns the complete Docker Compose YAML content as a string
@@ -35,24 +21,23 @@ type Composer interface {
3521
}
3622

3723
// NewComposer creates a new Composer based on the dataset type
38-
func NewComposer(ds dataset.Dataset, outdir string, parallelism int) Composer {
39-
if outdir == "" {
40-
outdir = ""
41-
}
42-
if parallelism <= 0 {
43-
parallelism = 4
44-
}
45-
46-
config := &ComposerConfig{
47-
OutDir: outdir,
48-
Parallelism: parallelism,
49-
}
50-
51-
switch v := ds.(type) {
24+
func NewComposer(ds dataset.Dataset, outDir string, parallelism int) Composer {
25+
if ds == nil {
26+
panic("dataset cannot be nil")
27+
}
28+
// Apply default output directory if not set
29+
if outDir == "" {
30+
outDir = "data/analysis"
31+
}
32+
// Apply default parallelism if non-positive
33+
if parallelism <= 0 {
34+
parallelism = 4
35+
}
36+
switch v := ds.(type) {
5237
case *dataset.VulnerabilityDataset:
53-
return NewVulComposer(v, config)
38+
return NewVulComposer(v, outDir, parallelism)
5439
case *dataset.ModuleDataset:
55-
return NewModComposer(v, config)
40+
return NewModComposer(v, outDir, parallelism)
5641
default:
5742
panic("unsupported dataset type")
5843
}

internal/compose/composer_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package compose
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"testing"
9+
10+
"github.com/ASSERT-KTH/go-cryptoapi/internal/dataset"
11+
)
12+
13+
func TestGenerateComposeHeader(t *testing.T) {
14+
want := "version: '3.8'\n\nservices:\n"
15+
if got := generateComposeHeader(); got != want {
16+
t.Errorf("generateComposeHeader() = %q; want %q", got, want)
17+
}
18+
}
19+
20+
func TestGenerateVolumeConfig(t *testing.T) {
21+
got := generateVolumeConfig()
22+
if !strings.Contains(got, "volumes:") {
23+
t.Error("generateVolumeConfig missing 'volumes:'")
24+
}
25+
if !strings.Contains(got, "driver: local") {
26+
t.Error("generateVolumeConfig missing 'driver: local'")
27+
}
28+
if !strings.Contains(got, "device: ${BASE_DIR}/gopher") {
29+
t.Error("generateVolumeConfig missing expected device path")
30+
}
31+
}
32+
33+
func TestGenerateServiceName(t *testing.T) {
34+
cases := []struct{ repo, id, want string }{
35+
{"github.com/user/repo", "123", "user-repo-123"},
36+
{"user/repo", "id", "user-repo-id"},
37+
{"example", "ID", "example-id"},
38+
}
39+
for _, c := range cases {
40+
if got := generateServiceName(c.repo, c.id); got != c.want {
41+
t.Errorf("generateServiceName(%q, %q) = %q; want %q", c.repo, c.id, got, c.want)
42+
}
43+
}
44+
}
45+
46+
func TestGenerateServiceStr(t *testing.T) {
47+
repo := "https://example.com/foo"
48+
gitTag := "v1.0.0"
49+
goVer := "1.15"
50+
svcName := "service-name"
51+
analysisDir := "analysis/dir"
52+
out := generateServiceStr(repo, gitTag, goVer, svcName, analysisDir)
53+
tests := []string{
54+
fmt.Sprintf(" %s:\n", svcName),
55+
"build:",
56+
"context: .",
57+
fmt.Sprintf("REPO_URL: \"%s\"", repo),
58+
fmt.Sprintf("GIT_TAG: \"%s\"", gitTag),
59+
fmt.Sprintf("GO_VERSION: \"%s\"", goVer),
60+
fmt.Sprintf("container_name: %s", svcName),
61+
"gopher-shared:/analysis/gopher",
62+
fmt.Sprintf("${BASE_DIR}/%s:/analysis/repo/scan_results", analysisDir),
63+
}
64+
for _, sub := range tests {
65+
if !strings.Contains(out, sub) {
66+
t.Errorf("generateServiceStr missing %q", sub)
67+
}
68+
}
69+
}
70+
71+
func TestWriteComposeFile(t *testing.T) {
72+
dir := t.TempDir()
73+
content := "hello compose"
74+
path, err := WriteComposeFile(dir, content)
75+
if err != nil {
76+
t.Fatalf("WriteComposeFile error: %v", err)
77+
}
78+
data, err := os.ReadFile(path)
79+
if err != nil {
80+
t.Fatalf("failed to read file: %v", err)
81+
}
82+
if string(data) != content {
83+
t.Errorf("file content = %q; want %q", string(data), content)
84+
}
85+
if filepath.Base(path) != "compose.yaml" {
86+
t.Errorf("file name = %q; want %q", filepath.Base(path), "compose.yaml")
87+
}
88+
}
89+
90+
func TestNewComposer(t *testing.T) {
91+
// ModuleDataset
92+
modDS := &dataset.ModuleDataset{Modules: []dataset.Module{}}
93+
cMod := NewComposer(modDS, "outdir", 2)
94+
mc, ok := cMod.(*ModComposer)
95+
if !ok {
96+
t.Fatalf("NewComposer(ModuleDataset) returned %T; want *ModComposer", cMod)
97+
}
98+
if mc.OutDir != "outdir" {
99+
t.Errorf("ModComposer.Config.OutDir = %q; want %q", mc.OutDir, "outdir")
100+
}
101+
if mc.Parallelism != 2 {
102+
t.Errorf("ModComposer.Config.Parallelism = %d; want %d", mc.Parallelism, 2)
103+
}
104+
// VulnerabilityDataset
105+
vulDS := &dataset.VulnerabilityDataset{Vulnerabilities: []dataset.Vulnerability{}}
106+
cVul := NewComposer(vulDS, "dir2", 3)
107+
vc, ok := cVul.(*VulComposer)
108+
if !ok {
109+
t.Fatalf("NewComposer(VulnerabilityDataset) returned %T; want *VulComposer", cVul)
110+
}
111+
if vc.OutDir != "dir2" {
112+
t.Errorf("VulComposer.Config.OutDir = %q; want %q", vc.OutDir, "dir2")
113+
}
114+
if vc.Parallelism != 3 {
115+
t.Errorf("VulComposer.Config.Parallelism = %d; want %d", vc.Parallelism, 3)
116+
}
117+
// Default parallelism when <= 0
118+
cDef := NewComposer(modDS, "", 0)
119+
mc2 := cDef.(*ModComposer)
120+
if mc2.Parallelism != 4 {
121+
t.Errorf("Default Parallelism = %d; want %d", mc2.Parallelism, 4)
122+
}
123+
}
124+
125+
// fakeDS implements dataset.Dataset but is not a ModuleDataset or VulnerabilityDataset
126+
type fakeDS struct{}
127+
func (f *fakeDS) Count() int { return 0 }
128+
func (f *fakeDS) Type() dataset.DatasetType { return "" }
129+
func (f *fakeDS) String() string { return "" }
130+
func (f *fakeDS) ID() string { return "" }
131+
132+
func TestNewComposerUnsupported(t *testing.T) {
133+
defer func() {
134+
if r := recover(); r == nil {
135+
t.Errorf("NewComposer did not panic for unsupported dataset")
136+
}
137+
}()
138+
_ = NewComposer(&fakeDS{}, "x", 1)
139+
}

internal/compose/mod_composer.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@ import (
1010

1111
// ModComposer implements the Composer interface for module datasets
1212
type ModComposer struct {
13-
Dataset *dataset.ModuleDataset
14-
Config *ComposerConfig
13+
Dataset *dataset.ModuleDataset
14+
OutDir string
15+
Parallelism int
1516
}
1617

17-
func NewModComposer(ds *dataset.ModuleDataset, config *ComposerConfig) *ModComposer {
18-
if config == nil {
19-
config = DefaultComposerConfig()
20-
}
18+
func NewModComposer(ds *dataset.ModuleDataset, outDir string, parallelism int) *ModComposer {
2119
return &ModComposer{
2220
Dataset: ds,
23-
Config: config,
21+
OutDir: outDir,
22+
Parallelism: parallelism,
2423
}
2524
}
2625

@@ -45,7 +44,7 @@ func (mc *ModComposer) addModServices(mod dataset.Module, datasetID string) stri
4544

4645
// Generate service name and paths
4746
serviceName := generateServiceName(mod.RepoName, "mod0-pkg1")
48-
analysisDir := filepath.Join(mc.Config.OutDir, mc.Dataset.ID(), serviceName)
47+
analysisDir := filepath.Join(mc.OutDir, mc.Dataset.ID(), serviceName)
4948

5049
// Add service configuration
5150
serviceStr := generateServiceStr(mod.URL, mod.ReleaseTag, mod.GoVersion, serviceName, analysisDir)
@@ -56,5 +55,5 @@ func (mc *ModComposer) addModServices(mod dataset.Module, datasetID string) stri
5655

5756
// RunCompose executes the Docker Compose configuration with parallelism
5857
func (mc *ModComposer) RunCompose(composeFilePath string, timeout time.Duration) error {
59-
return RunCompose(composeFilePath, mc.Config.Parallelism, timeout)
58+
return RunCompose(composeFilePath, mc.Parallelism, timeout)
6059
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package compose
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/ASSERT-KTH/go-cryptoapi/internal/dataset"
8+
)
9+
10+
// Test that ModComposer.ComposeStr includes the expected service block and args
11+
func TestModComposerComposeStr(t *testing.T) {
12+
ds := &dataset.ModuleDataset{Modules: []dataset.Module{{
13+
RepoName: "github.com/foo/bar",
14+
URL: "https://github.com/foo/bar",
15+
ReleaseTag: "v1.2.3",
16+
GoVersion: "1.15",
17+
}}}
18+
composer := NewModComposer(ds, "outdir", 4)
19+
yaml := composer.ComposeStr()
20+
// Header
21+
if !strings.HasPrefix(yaml, "version: '3.8'") {
22+
t.Errorf("ComposeStr missing header, got: %s", yaml)
23+
}
24+
// Expected service name for one module
25+
expectedService := "foo-bar-mod0-pkg1"
26+
if !strings.Contains(yaml, expectedService+":") {
27+
t.Errorf("ComposeStr missing service name %s", expectedService)
28+
}
29+
// Check that build args include REPO_URL, GIT_TAG, GO_VERSION
30+
if !strings.Contains(yaml, `REPO_URL: "https://github.com/foo/bar"`) {
31+
t.Errorf("ComposeStr missing REPO_URL argument")
32+
}
33+
if !strings.Contains(yaml, `GIT_TAG: "v1.2.3"`) {
34+
t.Errorf("ComposeStr missing GIT_TAG argument")
35+
}
36+
if !strings.Contains(yaml, `GO_VERSION: "1.15"`) {
37+
t.Errorf("ComposeStr missing GO_VERSION argument")
38+
}
39+
}

internal/compose/vul_composer.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,17 @@ const (
1717

1818
// VulComposer implements the Composer interface for vulnerability datasets
1919
type VulComposer struct {
20-
Dataset *dataset.VulnerabilityDataset
21-
Config *ComposerConfig
20+
Dataset *dataset.VulnerabilityDataset
21+
OutDir string
22+
Parallelism int
2223
//MetadataWriter *MetadataWriter
2324
}
2425

25-
func NewVulComposer(ds *dataset.VulnerabilityDataset, config *ComposerConfig) *VulComposer {
26-
if config == nil {
27-
config = DefaultComposerConfig()
28-
}
26+
func NewVulComposer(ds *dataset.VulnerabilityDataset, outdir string, parallelism int) *VulComposer {
2927
return &VulComposer{
30-
Dataset: ds,
31-
Config: config,
32-
//MetadataWriter: NewMetadataWriter(ds.GetDatasetIdentifier()),
28+
Dataset: ds,
29+
OutDir: outdir,
30+
Parallelism: parallelism,
3331
}
3432
}
3533

@@ -69,10 +67,10 @@ func (vc *VulComposer) addVulServices(vuln dataset.Vulnerability, datasetID stri
6967
pkgID := strconv.Itoa(pkgIndex + 1) // 1-based index for better readability
7068
uniqueID := fmt.Sprintf("%s-%s", vulnID, pkgID)
7169
serviceName := generateServiceName(vuln.Repo.RepoSlug, uniqueID)
72-
analysisDir := filepath.Join(vc.Config.OutDir, vc.Dataset.ID(), serviceName)
73-
70+
analysisDir := filepath.Join(vc.OutDir, vc.Dataset.ID(), serviceName)
71+
7472
// Write metadata for this package
75-
metadataWriter := log.NewMetadataWriter(vc.Config.OutDir)
73+
metadataWriter := log.NewMetadataWriter(vc.OutDir)
7674
if err := metadataWriter.WriteMetadata(vuln, pkg, serviceName); err != nil {
7775
fmt.Printf("Warning: failed to write metadata for %s: %v\n", serviceName, err)
7876
}
@@ -87,5 +85,5 @@ func (vc *VulComposer) addVulServices(vuln dataset.Vulnerability, datasetID stri
8785

8886
// RunCompose executes the Docker Compose configuration with parallelism
8987
func (vc *VulComposer) RunCompose(composeFilePath string, timeout time.Duration) error {
90-
return RunCompose(composeFilePath, vc.Config.Parallelism, timeout)
88+
return RunCompose(composeFilePath, vc.Parallelism, timeout)
9189
}

0 commit comments

Comments
 (0)