Skip to content

Commit

Permalink
fixed test
Browse files Browse the repository at this point in the history
  • Loading branch information
mfreeman451 committed Jan 21, 2025
1 parent b7815bd commit 19508ee
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 59 deletions.
56 changes: 35 additions & 21 deletions pkg/agent/sweep_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"log"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -105,30 +106,36 @@ func (s *SweepService) performSweep(ctx context.Context) error {
return fmt.Errorf("failed to generate targets: %w", err)
}

// Reset processor state
s.processor.Reset()
log.Printf("Starting sweep with %d targets", len(targets))

// Start the scan
results, err := s.scanner.Scan(ctx, targets)
if err != nil {
return fmt.Errorf("scan failed: %w", err)
}

s.processor.Reset()

// Process results as they come in
var processedCount int
for result := range results {
// Store the result
if err := s.store.SaveResult(ctx, &result); err != nil {
log.Printf("Failed to save result: %v", err)
continue
}
processedCount++

// Process the result
if err := s.processor.Process(&result); err != nil {
log.Printf("Failed to process result: %v", err)
continue
}

// Store the result
if err := s.store.SaveResult(ctx, &result); err != nil {
log.Printf("Failed to save result: %v", err)
continue
}
}

log.Printf("Sweep completed: processed %d results", processedCount)

return nil
}

Expand Down Expand Up @@ -167,32 +174,39 @@ func identifyService(port int) string {
*/

func (s *SweepService) GetStatus(ctx context.Context) (*proto.StatusResponse, error) {
if s == nil {
log.Printf("Warning: Sweep service not initialized")

return &proto.StatusResponse{
Available: false,
Message: "Sweep service not initialized",
ServiceName: "network_sweep",
ServiceType: "sweep",
}, nil
}

// Get current summary from processor
summary, err := s.processor.GetSummary(ctx)
if err != nil {
log.Printf("Error getting sweep summary: %v", err)
return nil, fmt.Errorf("failed to get sweep summary: %w", err)
}

if summary.LastSweep == 0 {
summary.LastSweep = time.Now().Unix()
}

// Convert to JSON for the message field
statusJSON, err := json.Marshal(summary)
data := struct {
Network string `json:"network"`
TotalHosts int `json:"total_hosts"`
AvailableHosts int `json:"available_hosts"`
LastSweep int64 `json:"last_sweep"`
Ports []models.PortCount `json:"ports"`
Hosts []models.HostResult `json:"hosts"`
}{
Network: strings.Join(s.config.Networks, ","),
TotalHosts: summary.TotalHosts,
AvailableHosts: summary.AvailableHosts,
LastSweep: summary.LastSweep,
Ports: summary.Ports,
Hosts: summary.Hosts,
}

statusJSON, err := json.Marshal(data)
if err != nil {
log.Printf("Error marshaling sweep status: %v", err)
return nil, fmt.Errorf("failed to marshal sweep status: %w", err)
}

// Return status response
return &proto.StatusResponse{
Available: true,
Message: string(statusJSON),
Expand Down
8 changes: 5 additions & 3 deletions pkg/models/sweeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ const (

// Target represents a network target to be scanned.
type Target struct {
Host string
Port int
Mode SweepMode
Host string
Port int
Mode SweepMode
Metadata map[string]interface{} // Additional metadata about the scan

}

// Result represents the outcome of a sweep against a target.
Expand Down
19 changes: 18 additions & 1 deletion pkg/scan/combined_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,32 @@ func NewCombinedScanner(timeout time.Duration, concurrency, icmpCount int) *Comb
}

// Scan performs the scanning operation for all targets.
// In pkg/scan/combined_scanner.go

func (s *CombinedScanner) Scan(ctx context.Context, targets []models.Target) (<-chan models.Result, error) {
if len(targets) == 0 {
empty := make(chan models.Result)
close(empty)

return empty, nil

Check failure on line 39 in pkg/scan/combined_scanner.go

View workflow job for this annotation

GitHub Actions / lint

return statements should not be cuddled if block has more than two lines (wsl)
}

// Calculate total hosts by counting unique IPs
uniqueHosts := make(map[string]struct{})
for _, target := range targets {
uniqueHosts[target.Host] = struct{}{}
}
totalHosts := len(uniqueHosts)

Check failure on line 47 in pkg/scan/combined_scanner.go

View workflow job for this annotation

GitHub Actions / lint

assignments should only be cuddled with other assignments (wsl)

separated := s.separateTargets(targets)
log.Printf("Scanning targets - TCP: %d, ICMP: %d, Unique Hosts: %d",
len(separated.tcp), len(separated.icmp), totalHosts)

// Pass total hosts count through result metadata
for i := range targets {
targets[i].Metadata = map[string]interface{}{
"total_hosts": totalHosts,
}
}

// Handle single scanner cases
if result := s.handleSingleScannerCase(ctx, separated); result != nil {
Expand Down
27 changes: 25 additions & 2 deletions pkg/scan/combined_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func TestCombinedScanner_ScanBasic(t *testing.T) {
}

// TestCombinedScanner_ScanMixed tests scanning with mixed target types.
// In pkg/scan/combined_scanner_test.go

func TestCombinedScanner_ScanMixed(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down Expand Up @@ -115,13 +117,34 @@ func TestCombinedScanner_ScanMixed(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, results)

gotResults := make([]models.Result, 0, len(targets)) // Pre-allocate with capacity
// Collect results
var gotResults []models.Result

Check failure on line 121 in pkg/scan/combined_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

Consider pre-allocating `gotResults` (prealloc)
for result := range results {
gotResults = append(gotResults, result)
}

// Should get exactly 2 results
require.Len(t, gotResults, 2)
assertResultsMatch(t, []models.Result{tcpResult, icmpResult}, gotResults)

// Create maps to match results by mode since order isn't guaranteed
expectedMap := map[models.SweepMode]models.Result{
models.ModeTCP: tcpResult,
models.ModeICMP: icmpResult,
}

gotMap := map[models.SweepMode]models.Result{}
for _, result := range gotResults {
gotMap[result.Target.Mode] = result
}

// Compare results by mode
for mode, expected := range expectedMap {
got, exists := gotMap[mode]
if assert.True(t, exists, "Missing result for mode %s", mode) {
assert.Equal(t, expected.Target, got.Target, "Target mismatch for mode %s", mode)
assert.Equal(t, expected.Available, got.Available, "Availability mismatch for mode %s", mode)
}
}
}

// TestCombinedScanner_ScanErrors tests error handling.
Expand Down
80 changes: 69 additions & 11 deletions pkg/sweeper/memory_processor.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Package sweeper pkg/sweeper/memory_processor.go
package sweeper

import (
"context"
"log"
"time"

"github.com/mfreeman451/serviceradar/pkg/models"
Expand All @@ -10,6 +11,7 @@ import (
// InMemoryProcessor implements ResultProcessor with in-memory state.
type InMemoryProcessor struct {
*BaseProcessor
firstSeenTimes map[string]time.Time

Check failure on line 14 in pkg/sweeper/memory_processor.go

View workflow job for this annotation

GitHub Actions / lint

field `firstSeenTimes` is unused (unused)
}

func NewInMemoryProcessor() ResultProcessor {
Expand All @@ -31,39 +33,50 @@ func (p *InMemoryProcessor) RUnlock() {

// Process updates the internal state of the InMemoryProcessor.
func (p *InMemoryProcessor) Process(result *models.Result) error {
// Handle total hosts from metadata if available
if result.Target.Metadata != nil {
if totalHosts, ok := result.Target.Metadata["total_hosts"].(int); ok {
p.totalHosts = totalHosts
}
}
p.mu.Lock()

Check failure on line 42 in pkg/sweeper/memory_processor.go

View workflow job for this annotation

GitHub Actions / lint

expressions should not be cuddled with blocks (wsl)
defer p.mu.Unlock()

// Update last sweep time
if result.LastSeen.After(p.lastSweepTime) {
p.lastSweepTime = result.LastSeen
log.Printf("Processing result: Host=%s, Port=%d, Mode=%s, Available=%v, Response Time=%v",
result.Target.Host, result.Target.Port, result.Target.Mode, result.Available, result.RespTime)

// Always update last sweep time to current time
p.lastSweepTime = time.Now()

// Early return if not a TCP scan or not available
if result.Target.Mode != models.ModeTCP {
return nil
}

// Update port counts
if result.Available && result.Target.Mode == models.ModeTCP {
p.portCounts[result.Target.Port]++
// Update total hosts count (this should happen for every unique host)
if _, exists := p.hostMap[result.Target.Host]; !exists {
p.totalHosts++
}

// Update host information
host, exists := p.hostMap[result.Target.Host]
if !exists {
p.totalHosts++
host = &models.HostResult{
Host: result.Target.Host,
FirstSeen: result.FirstSeen,
LastSeen: result.LastSeen,
Available: false,
PortResults: make([]*models.PortResult, 0),
}

p.hostMap[result.Target.Host] = host
}

// Update availability based on TCP scan results
if result.Available {
host.Available = true
}
p.portCounts[result.Target.Port]++

if result.Target.Mode == models.ModeTCP {
// Append port result for TCP mode
portResult := &models.PortResult{
Port: result.Target.Port,
Available: result.Available,
Expand All @@ -84,6 +97,51 @@ func (p *InMemoryProcessor) Process(result *models.Result) error {
return nil
}

func (p *InMemoryProcessor) GetSummary(ctx context.Context) (*models.SweepSummary, error) {

Check failure on line 100 in pkg/sweeper/memory_processor.go

View workflow job for this annotation

GitHub Actions / lint

unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
p.mu.RLock()
defer p.mu.RUnlock()

// Count available hosts
availableHosts := 0
offlineHosts := make([]models.HostResult, 0)
onlineHosts := make([]models.HostResult, 0)

for _, host := range p.hostMap {
if host.Available {
availableHosts++
onlineHosts = append(onlineHosts, *host)

Check failure on line 112 in pkg/sweeper/memory_processor.go

View workflow job for this annotation

GitHub Actions / lint

append only allowed to cuddle with appended value (wsl)
} else {
offlineHosts = append(offlineHosts, *host)
}
}

// Sort port counts
ports := make([]models.PortCount, 0, len(p.portCounts))
for port, count := range p.portCounts {
ports = append(ports, models.PortCount{
Port: port,
Available: count,
})
}

// Combine online and offline hosts, with offline hosts at the end
allHosts := append(onlineHosts, offlineHosts...)

Check failure on line 128 in pkg/sweeper/memory_processor.go

View workflow job for this annotation

GitHub Actions / lint

appendAssign: append result not assigned to the same slice (gocritic)

// Calculate total possible hosts from the CIDR ranges in the scan
actualTotalHosts := len(p.hostMap)
if actualTotalHosts == 0 {
actualTotalHosts = p.totalHosts
}

return &models.SweepSummary{
TotalHosts: actualTotalHosts,
AvailableHosts: availableHosts,
LastSweep: p.lastSweepTime.Unix(),
Ports: ports,
Hosts: allHosts,
}, nil
}

// Reset clears the internal state of the InMemoryProcessor.
func (p *InMemoryProcessor) Reset() {
p.mu.Lock()
Expand Down
7 changes: 4 additions & 3 deletions pkg/sweeper/memory_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,11 @@ func (s *InMemoryStore) SaveResult(ctx context.Context, result *models.Result) e
defer cancel()

for i := range s.results {
// if the same target already exists, overwrite
if s.results[i].Target == result.Target {
// Compare individual fields of Target instead of the whole struct
if s.results[i].Target.Host == result.Target.Host &&
s.results[i].Target.Port == result.Target.Port &&
s.results[i].Target.Mode == result.Target.Mode {
s.results[i] = *result

return nil
}
}
Expand Down
Loading

0 comments on commit 19508ee

Please sign in to comment.