three-word-networking 2.3.0

Convert IP addresses to memorable, family-friendly word groups. IPv4 = 3 words, IPv6 = 6 or 9 words. Perfect reconstruction with human-readable vocabulary.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
#![allow(unused_imports)]

use std::net::{IpAddr, SocketAddr};
use std::time::Instant;
/// Comprehensive integration tests for three-word networking
use three_word_networking::ThreeWordAdaptiveEncoder;

mod test_config;
use test_config::*;

/// Test complete encoding/decoding workflows
#[test]
fn test_complete_ipv4_workflow() {
    init_test_env();
    let generator = AddressGenerator::new();

    for addr in generator.ipv4_addresses() {
        let encoded = encode_ip_address(addr).expect("Encoding failed");
        let decoded = decode_words(&encoded).expect("Decoding failed");
        assert_encoding_roundtrip(addr, &encoded, &decoded);
    }
}

#[test]
fn test_complete_ipv6_workflow() {
    init_test_env();
    let generator = AddressGenerator::new();

    for addr in generator.ipv6_addresses() {
        let encoded = encode_ip_address(addr).expect("Encoding failed");
        let decoded = decode_words(&encoded).expect("Decoding failed");
        assert_encoding_roundtrip(addr, &encoded, &decoded);
    }
}

#[test]
fn test_socket_address_workflow() {
    init_test_env();
    let test_cases = vec![
        "127.0.0.1:8080",
        "192.168.1.1:443",
        "[::1]:8080",
        "[2001:db8::1]:443",
    ];

    for addr in test_cases {
        let encoded = encode_socket_address(addr).expect("Encoding failed");
        let decoded = decode_socket_address(&encoded).expect("Decoding failed");
        assert_encoding_roundtrip(addr, &encoded, &decoded);
    }
}

#[test]
fn test_ip_address_workflow() {
    init_test_env();
    let generator = AddressGenerator::new();

    // Test IPv4 addresses with ports
    for addr in generator.ipv4_with_ports() {
        let encoder = ThreeWordAdaptiveEncoder::new().unwrap();
        if let Ok(encoded) = encoder.encode(addr) {
            let decoded = encoder.decode(&encoded).expect("Decoding failed");
            assert_eq!(addr, &decoded, "Roundtrip failed for {}", addr);
        }
    }

    // Test IPv6 addresses with ports
    for addr in generator.ipv6_with_ports() {
        let encoder = ThreeWordAdaptiveEncoder::new().unwrap();
        if let Ok(encoded) = encoder.encode(addr) {
            let decoded = encoder.decode(&encoded).expect("Decoding failed");
            assert_eq!(addr, &decoded, "Roundtrip failed for {}", addr);
        }
    }
}

/// Test error handling and edge cases
#[test]
fn test_invalid_input_handling() {
    init_test_env();
    let invalid_inputs = vec![
        "invalid.ip.address",
        "999.999.999.999",
        "::gg",
        "not-an-ip",
        "",
        "127.0.0.1:99999",
    ];

    for input in invalid_inputs {
        match encode_ip_address(input) {
            Ok(_) => panic!("Expected error for invalid input: {}", input),
            Err(_) => {} // Expected
        }
    }
}

#[test]
fn test_word_validation() {
    init_test_env();
    let valid_words = vec!["apple orange banana", "one two three", "first second third"];

    for words in valid_words {
        let result = validate_word_format(words);
        assert!(
            result.is_ok(),
            "Valid words should pass validation: {}",
            words
        );
    }

    let invalid_words = vec![
        "one two",              // Too few words
        "one two three four",   // Too many words
        "one  two three",       // Double space (empty word)
        "",                     // Empty string
    ];

    for words in invalid_words {
        let result = validate_word_format(words);
        assert!(
            result.is_err(),
            "Invalid words should fail validation: {}",
            words
        );
    }
}

/// Test performance requirements
#[test]
fn test_encoding_performance() {
    init_test_env();
    let test_ip = "192.168.1.1";

    test_performance!(
        "IPv4 encoding",
        {
            let _ = encode_ip_address(test_ip).expect("Encoding failed");
        },
        100000
    ); // 100ms max (very lenient for debug builds)
}

#[test]
fn test_decoding_performance() {
    init_test_env();
    let test_ip = "192.168.1.1";
    let encoded = encode_ip_address(test_ip).expect("Encoding failed");

    test_performance!(
        "IPv4 decoding",
        {
            let _ = decode_words(&encoded).expect("Decoding failed");
        },
        50000
    ); // 50ms max (more lenient for debug builds)
}

#[test]
fn test_batch_processing_performance() {
    init_test_env();
    let generator = AddressGenerator::new();
    let addresses: Vec<_> = generator
        .ipv4_addresses()
        .iter()
        .chain(generator.ipv6_addresses().iter())
        .collect();

    let start = Instant::now();

    for addr in &addresses {
        let encoded = encode_ip_address(addr).expect("Encoding failed");
        let _decoded = decode_words(&encoded).expect("Decoding failed");
    }

    let duration = start.elapsed();
    let ops_per_sec = (addresses.len() as f64 * 2.0) / duration.as_secs_f64();

    // Should process at least 100 operations per second (more lenient for debug builds)
    assert!(
        ops_per_sec >= 100.0,
        "Batch processing too slow: {:.2} ops/sec",
        ops_per_sec
    );
}

/// Test memory usage and resource management
#[test]
fn test_memory_efficiency() {
    init_test_env();
    let generator = AddressGenerator::new();

    // Test that repeated operations don't cause memory leaks
    for _ in 0..1000 {
        for addr in generator.ipv4_addresses() {
            let encoded = encode_ip_address(addr).expect("Encoding failed");
            let _decoded = decode_words(&encoded).expect("Decoding failed");
        }
    }

    // If we get here without OOM, the test passes
    assert!(true);
}

/// Test concurrent access
#[test]
fn test_concurrent_encoding() {
    init_test_env();
    use std::sync::Arc;
    use std::thread;

    let generator = Arc::new(AddressGenerator::new());
    let mut handles = vec![];

    for _ in 0..10 {
        let generator_clone = Arc::clone(&generator);
        let handle = thread::spawn(move || {
            for addr in generator_clone.ipv4_addresses() {
                let encoded = encode_ip_address(addr).expect("Encoding failed");
                let decoded = decode_words(&encoded).expect("Decoding failed");
                assert_encoding_roundtrip(addr, &encoded, &decoded);
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().expect("Thread failed");
    }
}

/// Test with real-world data patterns
#[test]
fn test_real_world_patterns() {
    init_test_env();
    let real_world_addresses = real_world_data();

    for addr in real_world_addresses {
        let encoded = encode_ip_address(&addr).expect("Encoding failed");
        let decoded = decode_words(&encoded).expect("Decoding failed");
        assert_encoding_roundtrip(&addr, &encoded, &decoded);
    }
}

/// Test edge cases
#[test]
fn test_edge_cases() {
    init_test_env();
    let edge_cases = edge_case_data();

    for addr in edge_cases {
        let encoded = encode_ip_address(&addr).expect("Encoding failed");
        let decoded = decode_words(&encoded).expect("Decoding failed");
        assert_encoding_roundtrip(&addr, &encoded, &decoded);
    }
}

/// Test compression efficiency
#[test]
fn test_compression_efficiency() {
    init_test_env();
    let test_cases = vec![
        ("127.0.0.1", 48), // 3 words × 16 chars/word max = 48 bytes max
        ("192.168.1.1", 48),
        ("::1", 96), // IPv6 uses 6 words × 16 chars/word max = 96 bytes max
        ("2001:db8::1", 96),
    ];

    for (addr, expected_max_bytes) in test_cases {
        let encoded = encode_ip_address(addr).expect("Encoding failed");
        let byte_count = encoded.len(); // Actual string length in bytes

        assert!(
            byte_count <= expected_max_bytes,
            "Encoding too long for {}: {} bytes > {} bytes",
            addr,
            byte_count,
            expected_max_bytes
        );
    }
}

/// Test deterministic behavior
#[test]
fn test_deterministic_encoding() {
    init_test_env();
    let test_ip = "192.168.1.1";

    // Encode the same address multiple times
    let encodings: Vec<_> = (0..10)
        .map(|_| encode_ip_address(test_ip).expect("Encoding failed"))
        .collect();

    // All encodings should be identical
    let first = &encodings[0];
    for encoding in &encodings[1..] {
        assert_eq!(first, encoding, "Encoding should be deterministic");
    }
}

/// Test word quality and readability
#[test]
fn test_word_quality() {
    init_test_env();
    let test_ip = "192.168.1.1";
    let encoded = encode_ip_address(test_ip).expect("Encoding failed");
    let words: Vec<&str> = encoded.split(' ').collect();

    // Test word properties
    for word in words {
        assert!(word.len() >= 2, "Word too short: {}", word);
        // No maximum length restriction - frequency-based words can be longer
        assert!(
            word.chars().all(|c| c.is_ascii_lowercase()),
            "Word contains invalid characters: {}",
            word
        );
    }
}

/// Test error recovery
#[test]
fn test_error_recovery() {
    init_test_env();
    let malformed_words = vec![
        "one.two",                    // Missing word (need 3 for IPv4)
        "one.two.three.four",         // Extra word
        "zzzzz999.xxxxx888.qqqqq777", // Invalid words not in dictionary
        ".two.three",                 // Empty first word
        "one..three",                 // Empty middle word
    ];

    for words in malformed_words {
        match decode_words(words) {
            Ok(_) => panic!("Expected error for malformed words: {}", words),
            Err(e) => {
                // Error should be descriptive
                let error_msg = format!("{}", e);
                assert!(!error_msg.is_empty(), "Error message should not be empty");
                assert!(error_msg.len() > 10, "Error message should be descriptive");
            }
        }
    }
}

/// Test CLI integration
#[test]
fn test_cli_integration() {
    init_test_env();
    use std::process::Command;

    let test_ip = "192.168.1.1";

    // Test CLI encoding
    let output = Command::new("cargo")
        .args(&["run", "--bin", "3wn", "--", test_ip])
        .output()
        .expect("Failed to execute CLI");

    assert!(output.status.success(), "CLI encoding failed");

    let encoded = String::from_utf8(output.stdout)
        .expect("Invalid UTF-8 output")
        .trim()
        .to_string();

    // Test CLI decoding
    let output = Command::new("cargo")
        .args(&["run", "--bin", "3wn", "--", &encoded])
        .output()
        .expect("Failed to execute CLI");

    assert!(output.status.success(), "CLI decoding failed");

    let decoded = String::from_utf8(output.stdout)
        .expect("Invalid UTF-8 output")
        .trim()
        .to_string();

    // The CLI adds default port :80 to IPv4 addresses
    let expected = if !test_ip.contains(':') {
        format!("{}:80", test_ip)
    } else {
        test_ip.to_string()
    };
    assert_eq!(expected, decoded, "CLI roundtrip failed");
}

// Helper functions for testing
fn encode_ip_address(addr: &str) -> Result<String, Box<dyn std::error::Error>> {
    let encoder = ThreeWordAdaptiveEncoder::new()?;
    encoder.encode(addr).map_err(|e| e.into())
}

fn decode_words(words: &str) -> Result<String, Box<dyn std::error::Error>> {
    let encoder = ThreeWordAdaptiveEncoder::new()?;
    encoder.decode(words).map_err(|e| e.into())
}

fn encode_socket_address(addr: &str) -> Result<String, Box<dyn std::error::Error>> {
    let encoder = ThreeWordAdaptiveEncoder::new()?;
    encoder.encode(addr).map_err(|e| e.into())
}

fn decode_socket_address(words: &str) -> Result<String, Box<dyn std::error::Error>> {
    let encoder = ThreeWordAdaptiveEncoder::new()?;
    encoder.decode(words).map_err(|e| e.into())
}

// Multiaddr functions removed - using pure IP:port format only

fn validate_word_format(words: &str) -> Result<(), Box<dyn std::error::Error>> {
    let parts: Vec<&str> = words.split(' ').collect();
    if parts.len() != 3 {
        return Err(Box::new(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "Must have exactly 3 words",
        )));
    }

    for part in parts {
        if part.is_empty() {
            return Err(Box::new(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "Words cannot be empty",
            )));
        }
        if !part.chars().all(|c| c.is_ascii_lowercase()) {
            return Err(Box::new(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "Words can only contain lowercase letters and hyphens",
            )));
        }
    }

    Ok(())
}