Subversion Repositories havirt

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
47 rodolico 1
#!/usr/bin/env perl
2
 
3
# test_integration.pl
4
# Integration test script for havirt
5
# Tests the havirt modules and functions directly
6
#
7
# Usage: ./test_integration.pl [--verbose] [--dryrun]
8
#
9
# Copyright 2026 Daily Data, Inc.
10
 
11
use strict;
12
use warnings;
13
use FindBin;
14
use File::Spec;
15
use Cwd 'abs_path';
16
use File::Basename;
17
# Add parent directory to lib path (tests/../ to find modules)
18
use lib dirname( dirname( abs_path( __FILE__ ) ) );
19
 
20
use Data::Dumper;
21
use Getopt::Long;
22
 
23
# Load havirt modules
24
use havirt;
25
use domain;
26
use node;
27
use cluster;
28
 
29
# Test framework variables
30
my $tests_run = 0;
31
my $tests_passed = 0;
32
my $tests_failed = 0;
33
my @failed_tests;
34
 
35
# Command line options
36
my $verbose = 0;
37
my $dryrun = 1;  # Default to dryrun for safety
38
 
39
GetOptions(
40
    'verbose|v+' => \$verbose,
41
    'dryrun|n!' => \$dryrun,
42
) or die "Error parsing command line\n";
43
 
44
# Color codes for output
45
my $RED = "\033[0;31m";
46
my $GREEN = "\033[0;32m";
47
my $YELLOW = "\033[1;33m";
48
my $BLUE = "\033[0;34m";
49
my $NC = "\033[0m";
50
 
51
# Logging functions
52
sub log_info {
53
    print "${BLUE}[INFO]${NC} ", @_, "\n";
54
}
55
 
56
sub log_success {
57
    print "${GREEN}[PASS]${NC} ", @_, "\n";
58
    $tests_passed++;
59
}
60
 
61
sub log_failure {
62
    my $msg = shift;
63
    print "${RED}[FAIL]${NC} $msg\n";
64
    $tests_failed++;
65
    push @failed_tests, $msg;
66
}
67
 
68
sub log_test {
69
    print "${YELLOW}[TEST]${NC} ", @_, "\n";
70
    $tests_run++;
71
}
72
 
73
# Test function wrapper
74
sub run_test {
75
    my ($test_name, $code) = @_;
76
 
77
    log_test($test_name);
78
 
79
    eval {
80
        my $result = $code->();
81
        if (defined $result) {
82
            log_success($test_name);
83
            print "  Result: $result\n" if $verbose && $result;
84
        } else {
85
            log_success($test_name);
86
        }
87
    };
88
 
89
    if ($@) {
90
        log_failure("$test_name - Error: $@");
91
    }
92
}
93
 
94
# Test that a function returns defined value
95
sub test_returns_defined {
96
    my ($test_name, $code) = @_;
97
 
98
    log_test($test_name);
99
 
100
    eval {
101
        my $result = $code->();
102
        if (defined $result) {
103
            log_success($test_name);
104
            print "  Result: " . (ref($result) ? Dumper($result) : $result) . "\n" if $verbose > 1;
105
        } else {
106
            log_failure("$test_name - returned undefined");
107
        }
108
    };
109
 
110
    if ($@) {
111
        log_failure("$test_name - Error: $@");
112
    }
113
}
114
 
115
print "=" x 50 . "\n";
116
print "  havirt Integration Test Suite\n";
117
print "=" x 50 . "\n\n";
118
 
119
log_info("Running in " . ($dryrun ? "DRY-RUN" : "LIVE") . " mode");
120
log_info("Verbose level: $verbose");
121
print "\n";
122
 
123
# Initialize config (in parent directory)
124
my $configFileName = dirname($FindBin::RealBin) . '/config.yaml';
125
my $config;
126
 
127
print "=" x 50 . "\n";
128
print "Section 1: Configuration Tests\n";
129
print "=" x 50 . "\n\n";
130
 
131
run_test("Check config file exists", sub {
132
    return -f $configFileName ? "Config file found" : undef;
133
});
134
 
135
test_returns_defined("Load configuration", sub {
136
    $config = havirt::readConfig($configFileName);
137
    return $config ? "Config loaded" : undef;
138
});
139
 
140
# Set test flags
141
if ($config) {
142
    $config->{'flags'}->{'dryrun'} = $dryrun;
143
    $config->{'flags'}->{'verbose'} = $verbose;
144
    $main::config = $config;
145
}
146
 
147
print "\n";
148
 
149
# Test havirt.pm functions
150
print "=" x 50 . "\n";
151
print "Section 2: havirt.pm Core Functions\n";
152
print "=" x 50 . "\n\n";
153
 
154
my $statusDB;
155
 
156
test_returns_defined("Read status database", sub {
157
    $statusDB = havirt::readDB($config);
158
    $main::statusDB = $statusDB;
159
    return $statusDB ? "Status DB loaded" : undef;
160
});
161
 
162
if ($statusDB) {
163
    run_test("Check nodes in status DB", sub {
164
        my $node_count = scalar(keys %{$statusDB->{'node'}});
165
        return "Found $node_count nodes";
166
    });
167
 
168
    run_test("Check domains in status DB", sub {
169
        my $domain_count = scalar(keys %{$statusDB->{'domain'}});
170
        return "Found $domain_count domains";
171
    });
172
}
173
 
174
test_returns_defined("Test makeCommand function", sub {
175
    my $cmd = havirt::makeCommand("localhost", "test command");
176
    return $cmd;
177
});
178
 
179
run_test("Test diffArray function", sub {
180
    my @array1 = ('a', 'b', 'c', 'd');
181
    my @array2 = ('b', 'd', 'e');
182
    my @diff = havirt::diffArray(\@array1, \@array2);
183
    my $diff_str = join(',', @diff);
184
    return "Diff result: $diff_str" if @diff;
185
    return "Empty diff";
186
});
187
 
188
print "\n";
189
 
190
# Test node.pm functions
191
print "=" x 50 . "\n";
192
print "Section 3: node.pm Module Tests\n";
193
print "=" x 50 . "\n\n";
194
 
195
test_returns_defined("Node help", sub {
196
    my $help = node::help();
197
    return $help ? "Help returned" : undef;
198
});
199
 
200
test_returns_defined("Node list", sub {
201
    my $list = node::list();
202
    return $list ? "Node list returned" : undef;
203
});
204
 
205
if ($statusDB && $statusDB->{'node'}) {
206
    my @nodes = keys %{$statusDB->{'node'}};
207
    if (@nodes) {
208
        my $test_node = $nodes[0];
209
        log_info("Using node '$test_node' for tests");
210
 
211
        run_test("Get domains on node $test_node", sub {
212
            my @domains = havirt::getDomainsOnNode($statusDB, $test_node);
213
            return "Found " . scalar(@domains) . " domains on $test_node";
214
        });
215
    }
216
}
217
 
218
print "\n";
219
 
220
# Test domain.pm functions
221
print "=" x 50 . "\n";
222
print "Section 4: domain.pm Module Tests\n";
223
print "=" x 50 . "\n\n";
224
 
225
test_returns_defined("Domain help", sub {
226
    my $help = domain::help();
227
    return $help ? "Help returned" : undef;
228
});
229
 
230
test_returns_defined("Domain list", sub {
231
    my $list = domain::list();
232
    return $list ? "Domain list returned" : undef;
233
});
234
 
235
# Test MAC address generation
236
run_test("Test MAC address generation", sub {
237
    my $mac = domain::makeMac();
238
    return $mac =~ /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/ ? "Generated MAC: $mac" : undef;
239
});
240
 
241
# Test VNC port finding
242
run_test("Test VNC port finding", sub {
243
    my $port = domain::findVNCPort($statusDB);
244
    return $port && $port >= 5900 ? "Found VNC port: $port" : "Default port: 5900";
245
});
246
 
247
# Test domain parsing if config files exist
248
my $conf_dir = $config->{'config dir'} if $config;
249
if ($conf_dir && -d $conf_dir) {
250
    my @xml_files = glob("$conf_dir/*.xml");
251
    if (@xml_files) {
252
        my $test_xml = $xml_files[0];
253
        my $domain_name = basename($test_xml, '.xml');
254
 
255
        run_test("Parse domain config: $domain_name", sub {
256
            my $parsed = domain::parseDomain($test_xml);
257
            return $parsed ? "Parsed successfully" : undef;
258
        });
259
    }
260
}
261
 
262
print "\n";
263
 
264
# Test cluster.pm functions
265
print "=" x 50 . "\n";
266
print "Section 5: cluster.pm Module Tests\n";
267
print "=" x 50 . "\n\n";
268
 
269
test_returns_defined("Cluster help", sub {
270
    my $help = cluster::help();
271
    return $help ? "Help returned" : undef;
272
});
273
 
274
test_returns_defined("Cluster status", sub {
275
    my $status = cluster::status();
276
    return $status ? "Status returned" : undef;
277
});
278
 
279
run_test("Get cluster stats", sub {
280
    my $stats = cluster::getClusterStats($statusDB);
281
    if ($stats) {
282
        my $node_count = scalar(@{$stats->{'nodes'}}) if $stats->{'nodes'};
283
        return "Cluster stats retrieved" . ($node_count ? " ($node_count nodes)" : "");
284
    }
285
    return undef;
286
});
287
 
288
run_test("Test humanReadable function", sub {
289
    my $hr = cluster::humanReadable(1073741824); # 1GB in bytes
290
    return "1GB = $hr";
291
});
292
 
293
run_test("Test percent function", sub {
294
    my $pct = cluster::percent(50, 200);
295
    return "50/200 = $pct";
296
});
297
 
298
print "\n";
299
 
300
# Test data structure integrity
301
print "=" x 50 . "\n";
302
print "Section 6: Data Structure Validation\n";
303
print "=" x 50 . "\n\n";
304
 
305
if ($statusDB) {
306
    run_test("Validate node data structure", sub {
307
        foreach my $node_name (keys %{$statusDB->{'node'}}) {
308
            my $node = $statusDB->{'node'}{$node_name};
309
            return undef unless ref($node) eq 'HASH';
310
        }
311
        return "All node structures valid";
312
    });
313
 
314
    run_test("Validate domain data structure", sub {
315
        foreach my $domain_name (keys %{$statusDB->{'domain'}}) {
316
            my $domain = $statusDB->{'domain'}{$domain_name};
317
            return undef unless ref($domain) eq 'HASH';
318
        }
319
        return "All domain structures valid";
320
    });
321
 
322
    run_test("Check for orphaned domains", sub {
323
        my @orphaned;
324
        foreach my $domain_name (keys %{$statusDB->{'domain'}}) {
325
            my $domain = $statusDB->{'domain'}{$domain_name};
326
            if ($domain->{'node'} && !exists $statusDB->{'node'}{$domain->{'node'}}) {
327
                push @orphaned, $domain_name;
328
            }
329
        }
330
        if (@orphaned) {
331
            return "Found " . scalar(@orphaned) . " orphaned domains: " . join(', ', @orphaned);
332
        }
333
        return "No orphaned domains";
334
    });
335
}
336
 
337
print "\n";
338
 
339
# Final summary
340
print "=" x 50 . "\n";
341
print "  Test Summary\n";
342
print "=" x 50 . "\n\n";
343
 
344
print "Total tests run:    $tests_run\n";
345
print "Tests passed:       ${GREEN}$tests_passed${NC}\n";
346
print "Tests failed:       ${RED}$tests_failed${NC}\n";
347
print "\n";
348
 
349
if ($tests_failed > 0) {
350
    print "${RED}Failed tests:${NC}\n";
351
    foreach my $failed (@failed_tests) {
352
        print "  - $failed\n";
353
    }
354
    print "\n";
355
}
356
 
357
if ($tests_failed == 0) {
358
    print "${GREEN}All tests passed!${NC}\n";
359
    exit 0;
360
} else {
361
    print "${RED}Some tests failed.${NC}\n";
362
    exit 1;
363
}