• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

0xmichalis / nftbk / 18803745521

25 Oct 2025 01:29PM UTC coverage: 53.07% (+0.08%) from 52.991%
18803745521

push

github

0xmichalis
fix: fix make run-cli-test

3008 of 5668 relevant lines covered (53.07%)

9.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/cli/mod.rs
1
use std::path::PathBuf;
2

3
use anyhow::Result;
4
use clap::{Parser, Subcommand};
5

6
use crate::logging::LogLevel;
7

8
pub mod commands;
9
pub mod config;
10
pub mod x402;
11

12
#[derive(Parser, Debug)]
13
#[command(author, version, about, long_about = None)]
14
pub struct Cli {
15
    /// Set the log level
16
    #[arg(short, long, value_enum, default_value = "info")]
17
    pub log_level: LogLevel,
18

19
    /// Disable colored log output. NO_COLOR and FORCE_COLOR environment variables take precedence.
20
    #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
21
    pub no_color: bool,
22

23
    #[command(subcommand)]
24
    pub command: Commands,
25
}
26

27
#[derive(Subcommand, Debug)]
28
pub enum Commands {
29
    /// Create a backup locally
30
    Create {
31
        /// The path to the unified configuration file
32
        #[arg(short = 'c', long, default_value = "config.toml")]
33
        config_path: PathBuf,
34

35
        /// The path to the tokens configuration file
36
        #[arg(short = 't', long, default_value = "config_tokens.toml")]
37
        tokens_config_path: PathBuf,
38

39
        /// The directory to save the backup to
40
        #[arg(short, long, default_value = "nft_backup")]
41
        output_path: Option<PathBuf>,
42

43
        /// Delete redundant files in the backup folder
44
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
45
        prune_redundant: bool,
46

47
        /// Exit on the first error encountered
48
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
49
        exit_on_error: bool,
50

51
        /// Pin content to IPFS using configured providers
52
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
53
        pin_on_ipfs: bool,
54
    },
55
    /// Server-related operations
56
    Server {
57
        #[command(subcommand)]
58
        command: ServerCommands,
59
    },
60
}
61

62
#[derive(Subcommand, Debug)]
63
pub enum ServerCommands {
64
    /// Create a backup on the server
65
    Create {
66
        /// The path to the tokens configuration file
67
        #[arg(short = 't', long, default_value = "config_tokens.toml")]
68
        tokens_config_path: PathBuf,
69

70
        /// The server address to request backups from
71
        #[arg(long, default_value = "http://127.0.0.1:8080")]
72
        server_address: String,
73

74
        /// The directory to save the backup to
75
        #[arg(short, long, default_value = "nft_backup")]
76
        output_path: Option<PathBuf>,
77

78
        /// Force rerunning a completed backup task
79
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
80
        force: bool,
81

82
        /// User-Agent to send to the server (affects archive format)
83
        #[arg(long, default_value = "Linux")]
84
        user_agent: String,
85

86
        /// Request server to pin downloaded assets on IPFS
87
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
88
        pin_on_ipfs: bool,
89

90
        /// Polling interval in milliseconds for checking backup status
91
        #[arg(long, default_value = "10000")]
92
        polling_interval_ms: u64,
93
    },
94
    /// List existing backups on the server
95
    List {
96
        /// The server address to request backups from
97
        #[arg(long, default_value = "http://127.0.0.1:8080")]
98
        server_address: String,
99

100
        /// Show error details in the output table
101
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
102
        show_errors: bool,
103
    },
104
    /// Delete a backup from the server
105
    Delete {
106
        /// The server address to request backups from
107
        #[arg(long, default_value = "http://127.0.0.1:8080")]
108
        server_address: String,
109

110
        /// The task ID of the backup to delete
111
        #[arg(short = 't', long)]
112
        task_id: String,
113
    },
114
}
115

116
impl Cli {
117
    pub async fn run(self) -> Result<()> {
×
118
        match self.command {
×
119
            Commands::Create {
120
                config_path,
×
121
                tokens_config_path,
×
122
                output_path,
×
123
                prune_redundant,
×
124
                exit_on_error,
×
125
                pin_on_ipfs,
×
126
            } => {
127
                commands::create::run(
128
                    config_path,
×
129
                    tokens_config_path,
×
130
                    output_path,
×
131
                    prune_redundant,
×
132
                    exit_on_error,
×
133
                    pin_on_ipfs,
×
134
                )
135
                .await
×
136
            }
137
            Commands::Server { command } => match command {
×
138
                ServerCommands::Create {
139
                    tokens_config_path,
×
140
                    server_address,
×
141
                    output_path,
×
142
                    force,
×
143
                    user_agent,
×
144
                    pin_on_ipfs,
×
145
                    polling_interval_ms,
×
146
                } => {
147
                    commands::server::create::run(
148
                        tokens_config_path,
×
149
                        server_address,
×
150
                        output_path,
×
151
                        force,
×
152
                        user_agent,
×
153
                        pin_on_ipfs,
×
154
                        Some(polling_interval_ms),
×
155
                    )
156
                    .await
×
157
                }
158
                ServerCommands::List {
159
                    server_address,
×
160
                    show_errors,
×
161
                } => commands::server::list::run(server_address, show_errors).await,
×
162
                ServerCommands::Delete {
163
                    server_address,
×
164
                    task_id,
×
165
                } => commands::server::delete::run(server_address, task_id).await,
×
166
            },
167
        }
168
    }
169
}
170

171
#[cfg(test)]
172
mod tests {
173
    use super::*;
174
    use clap::Parser;
175

176
    mod cli_parsing_tests {
177
        use super::*;
178

179
        #[test]
180
        fn parses_create_command_with_defaults() {
181
            let args = vec!["nftbk-cli", "create"];
182
            let cli = Cli::try_parse_from(args).unwrap();
183

184
            assert_eq!(cli.log_level, LogLevel::Info);
185
            assert!(!cli.no_color);
186
            match cli.command {
187
                Commands::Create {
188
                    config_path,
189
                    tokens_config_path,
190
                    output_path,
191
                    prune_redundant,
192
                    exit_on_error,
193
                    pin_on_ipfs,
194
                } => {
195
                    assert_eq!(config_path, PathBuf::from("config.toml"));
196
                    assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
197
                    assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
198
                    assert!(!prune_redundant);
199
                    assert!(!exit_on_error);
200
                    assert!(!pin_on_ipfs);
201
                }
202
                _ => panic!("Expected Create command"),
203
            }
204
        }
205

206
        #[test]
207
        fn parses_create_command_with_custom_options() {
208
            let args = vec![
209
                "nftbk-cli",
210
                "--log-level",
211
                "debug",
212
                "--no-color",
213
                "true",
214
                "create",
215
                "--config-path",
216
                "custom_config.toml",
217
                "--tokens-config-path",
218
                "custom_tokens.toml",
219
                "--output-path",
220
                "/tmp/backup",
221
                "--prune-redundant",
222
                "true",
223
                "--exit-on-error",
224
                "true",
225
            ];
226
            let cli = Cli::try_parse_from(args).unwrap();
227

228
            assert_eq!(cli.log_level, LogLevel::Debug);
229
            assert!(cli.no_color);
230
            match cli.command {
231
                Commands::Create {
232
                    config_path,
233
                    tokens_config_path,
234
                    output_path,
235
                    prune_redundant,
236
                    exit_on_error,
237
                    pin_on_ipfs,
238
                } => {
239
                    assert_eq!(config_path, PathBuf::from("custom_config.toml"));
240
                    assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
241
                    assert_eq!(output_path, Some(PathBuf::from("/tmp/backup")));
242
                    assert!(prune_redundant);
243
                    assert!(exit_on_error);
244
                    assert!(!pin_on_ipfs);
245
                }
246
                _ => panic!("Expected Create command"),
247
            }
248
        }
249

250
        #[test]
251
        fn parses_server_create_command_with_defaults() {
252
            let args = vec!["nftbk-cli", "server", "create"];
253
            let cli = Cli::try_parse_from(args).unwrap();
254

255
            match cli.command {
256
                Commands::Server { command } => match command {
257
                    ServerCommands::Create {
258
                        tokens_config_path,
259
                        server_address,
260
                        output_path,
261
                        force,
262
                        user_agent,
263
                        pin_on_ipfs,
264
                        polling_interval_ms,
265
                    } => {
266
                        assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
267
                        assert_eq!(server_address, "http://127.0.0.1:8080");
268
                        assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
269
                        assert!(!force);
270
                        assert_eq!(user_agent, "Linux");
271
                        assert!(!pin_on_ipfs);
272
                        assert_eq!(polling_interval_ms, 10000);
273
                    }
274
                    _ => panic!("Expected Server Create command"),
275
                },
276
                _ => panic!("Expected Server command"),
277
            }
278
        }
279

280
        #[test]
281
        fn parses_server_create_command_with_custom_options() {
282
            let args = vec![
283
                "nftbk-cli",
284
                "server",
285
                "create",
286
                "--tokens-config-path",
287
                "custom_tokens.toml",
288
                "--server-address",
289
                "https://api.example.com",
290
                "--output-path",
291
                "/tmp/server_backup",
292
                "--force",
293
                "true",
294
                "--user-agent",
295
                "CustomAgent/1.0",
296
                "--pin-on-ipfs",
297
                "true",
298
            ];
299
            let cli = Cli::try_parse_from(args).unwrap();
300

301
            match cli.command {
302
                Commands::Server { command } => match command {
303
                    ServerCommands::Create {
304
                        tokens_config_path,
305
                        server_address,
306
                        output_path,
307
                        force,
308
                        user_agent,
309
                        pin_on_ipfs,
310
                        polling_interval_ms,
311
                    } => {
312
                        assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
313
                        assert_eq!(server_address, "https://api.example.com");
314
                        assert_eq!(output_path, Some(PathBuf::from("/tmp/server_backup")));
315
                        assert!(force);
316
                        assert_eq!(user_agent, "CustomAgent/1.0");
317
                        assert!(pin_on_ipfs);
318
                        assert_eq!(polling_interval_ms, 10000); // Default value
319
                    }
320
                    _ => panic!("Expected Server Create command"),
321
                },
322
                _ => panic!("Expected Server command"),
323
            }
324
        }
325

326
        #[test]
327
        fn parses_server_list_command_with_defaults() {
328
            let args = vec!["nftbk-cli", "server", "list"];
329
            let cli = Cli::try_parse_from(args).unwrap();
330

331
            match cli.command {
332
                Commands::Server { command } => match command {
333
                    ServerCommands::List {
334
                        server_address,
335
                        show_errors,
336
                    } => {
337
                        assert_eq!(server_address, "http://127.0.0.1:8080");
338
                        assert!(!show_errors);
339
                    }
340
                    _ => panic!("Expected Server List command"),
341
                },
342
                _ => panic!("Expected Server command"),
343
            }
344
        }
345

346
        #[test]
347
        fn parses_server_list_command_with_custom_server() {
348
            let args = vec![
349
                "nftbk-cli",
350
                "server",
351
                "list",
352
                "--server-address",
353
                "https://api.example.com",
354
            ];
355
            let cli = Cli::try_parse_from(args).unwrap();
356

357
            match cli.command {
358
                Commands::Server { command } => match command {
359
                    ServerCommands::List {
360
                        server_address,
361
                        show_errors,
362
                    } => {
363
                        assert_eq!(server_address, "https://api.example.com");
364
                        assert!(!show_errors);
365
                    }
366
                    _ => panic!("Expected Server List command"),
367
                },
368
                _ => panic!("Expected Server command"),
369
            }
370
        }
371

372
        #[test]
373
        fn parses_server_list_command_with_show_errors() {
374
            let args = vec!["nftbk-cli", "server", "list", "--show-errors", "true"];
375
            let cli = Cli::try_parse_from(args).unwrap();
376

377
            match cli.command {
378
                Commands::Server { command } => match command {
379
                    ServerCommands::List {
380
                        server_address,
381
                        show_errors,
382
                    } => {
383
                        assert_eq!(server_address, "http://127.0.0.1:8080");
384
                        assert!(show_errors);
385
                    }
386
                    _ => panic!("Expected Server List command"),
387
                },
388
                _ => panic!("Expected Server command"),
389
            }
390
        }
391

392
        #[test]
393
        fn parses_server_delete_command_with_defaults() {
394
            let args = vec![
395
                "nftbk-cli",
396
                "server",
397
                "delete",
398
                "--task-id",
399
                "test-task-123",
400
            ];
401
            let cli = Cli::try_parse_from(args).unwrap();
402

403
            match cli.command {
404
                Commands::Server { command } => match command {
405
                    ServerCommands::Delete {
406
                        server_address,
407
                        task_id,
408
                    } => {
409
                        assert_eq!(server_address, "http://127.0.0.1:8080");
410
                        assert_eq!(task_id, "test-task-123");
411
                    }
412
                    _ => panic!("Expected Server Delete command"),
413
                },
414
                _ => panic!("Expected Server command"),
415
            }
416
        }
417

418
        #[test]
419
        fn parses_server_delete_command_with_custom_options() {
420
            let args = vec![
421
                "nftbk-cli",
422
                "server",
423
                "delete",
424
                "--server-address",
425
                "https://api.example.com",
426
                "--task-id",
427
                "custom-task-456",
428
            ];
429
            let cli = Cli::try_parse_from(args).unwrap();
430

431
            match cli.command {
432
                Commands::Server { command } => match command {
433
                    ServerCommands::Delete {
434
                        server_address,
435
                        task_id,
436
                    } => {
437
                        assert_eq!(server_address, "https://api.example.com");
438
                        assert_eq!(task_id, "custom-task-456");
439
                    }
440
                    _ => panic!("Expected Server Delete command"),
441
                },
442
                _ => panic!("Expected Server command"),
443
            }
444
        }
445

446
        #[test]
447
        fn parses_all_log_levels() {
448
            for (level_str, expected_level) in [
449
                ("debug", LogLevel::Debug),
450
                ("info", LogLevel::Info),
451
                ("warn", LogLevel::Warn),
452
                ("error", LogLevel::Error),
453
            ] {
454
                let args = vec!["nftbk-cli", "--log-level", level_str, "create"];
455
                let cli = Cli::try_parse_from(args).unwrap();
456
                assert_eq!(cli.log_level, expected_level);
457
            }
458
        }
459

460
        #[test]
461
        fn handles_no_color_flag() {
462
            let args = vec!["nftbk-cli", "--no-color", "true", "create"];
463
            let cli = Cli::try_parse_from(args).unwrap();
464
            assert!(cli.no_color);
465

466
            let args = vec!["nftbk-cli", "--no-color", "false", "create"];
467
            let cli = Cli::try_parse_from(args).unwrap();
468
            assert!(!cli.no_color);
469
        }
470

471
        #[test]
472
        fn requires_subcommand() {
473
            let args = vec!["nftbk-cli"];
474
            let result = Cli::try_parse_from(args);
475
            assert!(result.is_err());
476
        }
477

478
        #[test]
479
        fn validates_log_level_enum() {
480
            let args = vec!["nftbk-cli", "--log-level", "invalid", "create"];
481
            let result = Cli::try_parse_from(args);
482
            assert!(result.is_err());
483
        }
484
    }
485
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc