1use serde::{Deserialize, Serialize};
8use chrono::{DateTime, Utc, NaiveDate};
9use std::collections::HashMap;
10use quick_xml::Reader;
11use quick_xml::events::Event;
12
13use crate::{VeracodeClient, VeracodeError};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Build {
18 pub build_id: String,
20 pub app_id: String,
22 pub version: Option<String>,
24 pub app_name: Option<String>,
26 pub sandbox_id: Option<String>,
28 pub sandbox_name: Option<String>,
30 pub lifecycle_stage: Option<String>,
32 pub launch_date: Option<NaiveDate>,
34 pub submitter: Option<String>,
36 pub platform: Option<String>,
38 pub analysis_unit: Option<String>,
40 pub policy_name: Option<String>,
42 pub policy_version: Option<String>,
44 pub policy_compliance_status: Option<String>,
46 pub rules_status: Option<String>,
48 pub grace_period_expired: Option<bool>,
50 pub scan_overdue: Option<bool>,
52 pub policy_updated_date: Option<DateTime<Utc>>,
54 pub legacy_scan_engine: Option<bool>,
56 pub attributes: HashMap<String, String>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct BuildList {
63 pub account_id: Option<String>,
65 pub app_id: String,
67 pub app_name: Option<String>,
69 pub builds: Vec<Build>,
71}
72
73#[derive(Debug, Clone)]
75pub struct CreateBuildRequest {
76 pub app_id: String,
78 pub version: Option<String>,
80 pub lifecycle_stage: Option<String>,
82 pub launch_date: Option<String>,
84 pub sandbox_id: Option<String>,
86}
87
88#[derive(Debug, Clone)]
90pub struct UpdateBuildRequest {
91 pub app_id: String,
93 pub build_id: Option<String>,
95 pub version: Option<String>,
97 pub lifecycle_stage: Option<String>,
99 pub launch_date: Option<String>,
101 pub sandbox_id: Option<String>,
103}
104
105#[derive(Debug, Clone)]
107pub struct DeleteBuildRequest {
108 pub app_id: String,
110 pub sandbox_id: Option<String>,
112}
113
114#[derive(Debug, Clone)]
116pub struct GetBuildInfoRequest {
117 pub app_id: String,
119 pub build_id: Option<String>,
121 pub sandbox_id: Option<String>,
123}
124
125#[derive(Debug, Clone)]
127pub struct GetBuildListRequest {
128 pub app_id: String,
130 pub sandbox_id: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct DeleteBuildResult {
137 pub result: String,
139}
140
141#[derive(Debug)]
143pub enum BuildError {
144 Api(VeracodeError),
146 BuildNotFound,
148 ApplicationNotFound,
150 SandboxNotFound,
152 InvalidParameter(String),
154 CreationFailed(String),
156 UpdateFailed(String),
158 DeletionFailed(String),
160 XmlParsingError(String),
162 Unauthorized,
164 PermissionDenied,
166 BuildInProgress,
168}
169
170impl std::fmt::Display for BuildError {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 match self {
173 BuildError::Api(err) => write!(f, "API error: {err}"),
174 BuildError::BuildNotFound => write!(f, "Build not found"),
175 BuildError::ApplicationNotFound => write!(f, "Application not found"),
176 BuildError::SandboxNotFound => write!(f, "Sandbox not found"),
177 BuildError::InvalidParameter(msg) => write!(f, "Invalid parameter: {msg}"),
178 BuildError::CreationFailed(msg) => write!(f, "Build creation failed: {msg}"),
179 BuildError::UpdateFailed(msg) => write!(f, "Build update failed: {msg}"),
180 BuildError::DeletionFailed(msg) => write!(f, "Build deletion failed: {msg}"),
181 BuildError::XmlParsingError(msg) => write!(f, "XML parsing error: {msg}"),
182 BuildError::Unauthorized => write!(f, "Unauthorized access"),
183 BuildError::PermissionDenied => write!(f, "Permission denied"),
184 BuildError::BuildInProgress => write!(f, "Build in progress, cannot modify"),
185 }
186 }
187}
188
189impl std::error::Error for BuildError {}
190
191impl From<VeracodeError> for BuildError {
192 fn from(err: VeracodeError) -> Self {
193 BuildError::Api(err)
194 }
195}
196
197impl From<std::io::Error> for BuildError {
198 fn from(err: std::io::Error) -> Self {
199 BuildError::Api(VeracodeError::InvalidResponse(err.to_string()))
200 }
201}
202
203impl From<reqwest::Error> for BuildError {
204 fn from(err: reqwest::Error) -> Self {
205 BuildError::Api(VeracodeError::Http(err))
206 }
207}
208
209pub struct BuildApi {
211 client: VeracodeClient,
212}
213
214impl BuildApi {
215 pub fn new(client: VeracodeClient) -> Self {
217 Self { client }
218 }
219
220 pub async fn create_build(&self, request: CreateBuildRequest) -> Result<Build, BuildError> {
230 let endpoint = "api/5.0/createbuild.do";
231
232 let mut query_params = Vec::new();
234 query_params.push(("app_id", request.app_id.as_str()));
235
236 if let Some(version) = &request.version {
237 query_params.push(("version", version.as_str()));
238 }
239
240 if let Some(lifecycle_stage) = &request.lifecycle_stage {
241 query_params.push(("lifecycle_stage", lifecycle_stage.as_str()));
242 }
243
244 if let Some(launch_date) = &request.launch_date {
245 query_params.push(("launch_date", launch_date.as_str()));
246 }
247
248 if let Some(sandbox_id) = &request.sandbox_id {
249 query_params.push(("sandbox_id", sandbox_id.as_str()));
250 }
251
252 let response = self.client.post_with_query_params(endpoint, &query_params).await?;
253
254 let status = response.status().as_u16();
255 match status {
256 200 => {
257 let response_text = response.text().await?;
258 self.parse_build_info(&response_text)
259 }
260 400 => {
261 let error_text = response.text().await.unwrap_or_default();
262 Err(BuildError::InvalidParameter(error_text))
263 }
264 401 => Err(BuildError::Unauthorized),
265 403 => Err(BuildError::PermissionDenied),
266 404 => Err(BuildError::ApplicationNotFound),
267 _ => {
268 let error_text = response.text().await.unwrap_or_default();
269 Err(BuildError::CreationFailed(format!("HTTP {status}: {error_text}")))
270 }
271 }
272 }
273
274 pub async fn update_build(&self, request: UpdateBuildRequest) -> Result<Build, BuildError> {
284 let endpoint = "api/5.0/updatebuild.do";
285
286 let mut query_params = Vec::new();
288 query_params.push(("app_id", request.app_id.as_str()));
289
290 if let Some(build_id) = &request.build_id {
291 query_params.push(("build_id", build_id.as_str()));
292 }
293
294 if let Some(version) = &request.version {
295 query_params.push(("version", version.as_str()));
296 }
297
298 if let Some(lifecycle_stage) = &request.lifecycle_stage {
299 query_params.push(("lifecycle_stage", lifecycle_stage.as_str()));
300 }
301
302 if let Some(launch_date) = &request.launch_date {
303 query_params.push(("launch_date", launch_date.as_str()));
304 }
305
306 if let Some(sandbox_id) = &request.sandbox_id {
307 query_params.push(("sandbox_id", sandbox_id.as_str()));
308 }
309
310 let response = self.client.post_with_query_params(endpoint, &query_params).await?;
311
312 let status = response.status().as_u16();
313 match status {
314 200 => {
315 let response_text = response.text().await?;
316 self.parse_build_info(&response_text)
317 }
318 400 => {
319 let error_text = response.text().await.unwrap_or_default();
320 Err(BuildError::InvalidParameter(error_text))
321 }
322 401 => Err(BuildError::Unauthorized),
323 403 => Err(BuildError::PermissionDenied),
324 404 => {
325 if request.sandbox_id.is_some() {
326 Err(BuildError::SandboxNotFound)
327 } else {
328 Err(BuildError::BuildNotFound)
329 }
330 }
331 _ => {
332 let error_text = response.text().await.unwrap_or_default();
333 Err(BuildError::UpdateFailed(format!("HTTP {status}: {error_text}")))
334 }
335 }
336 }
337
338 pub async fn delete_build(&self, request: DeleteBuildRequest) -> Result<DeleteBuildResult, BuildError> {
348 let endpoint = "api/5.0/deletebuild.do";
349
350 let mut query_params = Vec::new();
352 query_params.push(("app_id", request.app_id.as_str()));
353
354 if let Some(sandbox_id) = &request.sandbox_id {
355 query_params.push(("sandbox_id", sandbox_id.as_str()));
356 }
357
358 let response = self.client.post_with_query_params(endpoint, &query_params).await?;
359
360 let status = response.status().as_u16();
361 match status {
362 200 => {
363 let response_text = response.text().await?;
364 self.parse_delete_result(&response_text)
365 }
366 400 => {
367 let error_text = response.text().await.unwrap_or_default();
368 Err(BuildError::InvalidParameter(error_text))
369 }
370 401 => Err(BuildError::Unauthorized),
371 403 => Err(BuildError::PermissionDenied),
372 404 => {
373 if request.sandbox_id.is_some() {
374 Err(BuildError::SandboxNotFound)
375 } else {
376 Err(BuildError::BuildNotFound)
377 }
378 }
379 _ => {
380 let error_text = response.text().await.unwrap_or_default();
381 Err(BuildError::DeletionFailed(format!("HTTP {status}: {error_text}")))
382 }
383 }
384 }
385
386 pub async fn get_build_info(&self, request: GetBuildInfoRequest) -> Result<Build, BuildError> {
396 let endpoint = "api/5.0/getbuildinfo.do";
397
398 let mut query_params = Vec::new();
400 query_params.push(("app_id", request.app_id.as_str()));
401
402 if let Some(build_id) = &request.build_id {
403 query_params.push(("build_id", build_id.as_str()));
404 }
405
406 if let Some(sandbox_id) = &request.sandbox_id {
407 query_params.push(("sandbox_id", sandbox_id.as_str()));
408 }
409
410 let response = self.client.get_with_query_params(endpoint, &query_params).await?;
411
412 let status = response.status().as_u16();
413 match status {
414 200 => {
415 let response_text = response.text().await?;
416 self.parse_build_info(&response_text)
417 }
418 400 => {
419 let error_text = response.text().await.unwrap_or_default();
420 Err(BuildError::InvalidParameter(error_text))
421 }
422 401 => Err(BuildError::Unauthorized),
423 403 => Err(BuildError::PermissionDenied),
424 404 => {
425 if request.sandbox_id.is_some() {
426 Err(BuildError::SandboxNotFound)
427 } else {
428 Err(BuildError::BuildNotFound)
429 }
430 }
431 _ => {
432 let error_text = response.text().await.unwrap_or_default();
433 Err(BuildError::Api(VeracodeError::InvalidResponse(format!("HTTP {status}: {error_text}"))))
434 }
435 }
436 }
437
438 pub async fn get_build_list(&self, request: GetBuildListRequest) -> Result<BuildList, BuildError> {
448 let endpoint = "api/5.0/getbuildlist.do";
449
450 let mut query_params = Vec::new();
452 query_params.push(("app_id", request.app_id.as_str()));
453
454 if let Some(sandbox_id) = &request.sandbox_id {
455 query_params.push(("sandbox_id", sandbox_id.as_str()));
456 }
457
458 let response = self.client.get_with_query_params(endpoint, &query_params).await?;
459
460 let status = response.status().as_u16();
461 match status {
462 200 => {
463 let response_text = response.text().await?;
464 self.parse_build_list(&response_text)
465 }
466 400 => {
467 let error_text = response.text().await.unwrap_or_default();
468 Err(BuildError::InvalidParameter(error_text))
469 }
470 401 => Err(BuildError::Unauthorized),
471 403 => Err(BuildError::PermissionDenied),
472 404 => {
473 if request.sandbox_id.is_some() {
474 Err(BuildError::SandboxNotFound)
475 } else {
476 Err(BuildError::ApplicationNotFound)
477 }
478 }
479 _ => {
480 let error_text = response.text().await.unwrap_or_default();
481 Err(BuildError::Api(VeracodeError::InvalidResponse(format!("HTTP {status}: {error_text}"))))
482 }
483 }
484 }
485
486 fn parse_build_info(&self, xml: &str) -> Result<Build, BuildError> {
488 let mut reader = Reader::from_str(xml);
489 reader.config_mut().trim_text(true);
490
491 let mut buf = Vec::new();
492 let mut build = Build {
493 build_id: String::new(),
494 app_id: String::new(),
495 version: None,
496 app_name: None,
497 sandbox_id: None,
498 sandbox_name: None,
499 lifecycle_stage: None,
500 launch_date: None,
501 submitter: None,
502 platform: None,
503 analysis_unit: None,
504 policy_name: None,
505 policy_version: None,
506 policy_compliance_status: None,
507 rules_status: None,
508 grace_period_expired: None,
509 scan_overdue: None,
510 policy_updated_date: None,
511 legacy_scan_engine: None,
512 attributes: HashMap::new(),
513 };
514
515 loop {
516 match reader.read_event_into(&mut buf) {
517 Ok(Event::Start(ref e)) => {
518 if e.name().as_ref() == b"build" {
519 for attr in e.attributes() {
520 if let Ok(attr) = attr {
521 let key = String::from_utf8_lossy(attr.key.as_ref());
522 let value = String::from_utf8_lossy(&attr.value);
523
524 match key.as_ref() {
525 "build_id" => build.build_id = value.to_string(),
526 "app_id" => build.app_id = value.to_string(),
527 "version" => build.version = Some(value.to_string()),
528 "app_name" => build.app_name = Some(value.to_string()),
529 "sandbox_id" => build.sandbox_id = Some(value.to_string()),
530 "sandbox_name" => build.sandbox_name = Some(value.to_string()),
531 "lifecycle_stage" => build.lifecycle_stage = Some(value.to_string()),
532 "submitter" => build.submitter = Some(value.to_string()),
533 "platform" => build.platform = Some(value.to_string()),
534 "analysis_unit" => build.analysis_unit = Some(value.to_string()),
535 "policy_name" => build.policy_name = Some(value.to_string()),
536 "policy_version" => build.policy_version = Some(value.to_string()),
537 "policy_compliance_status" => build.policy_compliance_status = Some(value.to_string()),
538 "rules_status" => build.rules_status = Some(value.to_string()),
539 "grace_period_expired" => {
540 build.grace_period_expired = value.parse::<bool>().ok();
541 }
542 "scan_overdue" => {
543 build.scan_overdue = value.parse::<bool>().ok();
544 }
545 "legacy_scan_engine" => {
546 build.legacy_scan_engine = value.parse::<bool>().ok();
547 }
548 "launch_date" => {
549 if let Ok(date) = NaiveDate::parse_from_str(&value, "%m/%d/%Y") {
550 build.launch_date = Some(date);
551 }
552 }
553 "policy_updated_date" => {
554 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&value) {
555 build.policy_updated_date = Some(datetime.with_timezone(&Utc));
556 }
557 }
558 _ => {
559 build.attributes.insert(key.to_string(), value.to_string());
560 }
561 }
562 }
563 }
564 }
565 }
566 Ok(Event::Eof) => break,
567 Err(e) => return Err(BuildError::XmlParsingError(e.to_string())),
568 _ => {}
569 }
570 buf.clear();
571 }
572
573 if build.build_id.is_empty() {
574 return Err(BuildError::XmlParsingError("No build information found in response".to_string()));
575 }
576
577 Ok(build)
578 }
579
580 fn parse_build_list(&self, xml: &str) -> Result<BuildList, BuildError> {
582 let mut reader = Reader::from_str(xml);
583 reader.config_mut().trim_text(true);
584
585 let mut buf = Vec::new();
586 let mut build_list = BuildList {
587 account_id: None,
588 app_id: String::new(),
589 app_name: None,
590 builds: Vec::new(),
591 };
592
593 loop {
594 match reader.read_event_into(&mut buf) {
595 Ok(Event::Start(ref e)) => {
596 match e.name().as_ref() {
597 b"buildlist" => {
598 for attr in e.attributes() {
599 if let Ok(attr) = attr {
600 let key = String::from_utf8_lossy(attr.key.as_ref());
601 let value = String::from_utf8_lossy(&attr.value);
602
603 match key.as_ref() {
604 "account_id" => build_list.account_id = Some(value.to_string()),
605 "app_id" => build_list.app_id = value.to_string(),
606 "app_name" => build_list.app_name = Some(value.to_string()),
607 _ => {}
608 }
609 }
610 }
611 }
612 b"build" => {
613 let mut build = Build {
614 build_id: String::new(),
615 app_id: build_list.app_id.clone(),
616 version: None,
617 app_name: build_list.app_name.clone(),
618 sandbox_id: None,
619 sandbox_name: None,
620 lifecycle_stage: None,
621 launch_date: None,
622 submitter: None,
623 platform: None,
624 analysis_unit: None,
625 policy_name: None,
626 policy_version: None,
627 policy_compliance_status: None,
628 rules_status: None,
629 grace_period_expired: None,
630 scan_overdue: None,
631 policy_updated_date: None,
632 legacy_scan_engine: None,
633 attributes: HashMap::new(),
634 };
635
636 for attr in e.attributes() {
637 if let Ok(attr) = attr {
638 let key = String::from_utf8_lossy(attr.key.as_ref());
639 let value = String::from_utf8_lossy(&attr.value);
640
641 match key.as_ref() {
642 "build_id" => build.build_id = value.to_string(),
643 "version" => build.version = Some(value.to_string()),
644 "sandbox_id" => build.sandbox_id = Some(value.to_string()),
645 "sandbox_name" => build.sandbox_name = Some(value.to_string()),
646 "lifecycle_stage" => build.lifecycle_stage = Some(value.to_string()),
647 "submitter" => build.submitter = Some(value.to_string()),
648 "platform" => build.platform = Some(value.to_string()),
649 "analysis_unit" => build.analysis_unit = Some(value.to_string()),
650 "policy_name" => build.policy_name = Some(value.to_string()),
651 "policy_version" => build.policy_version = Some(value.to_string()),
652 "policy_compliance_status" => build.policy_compliance_status = Some(value.to_string()),
653 "rules_status" => build.rules_status = Some(value.to_string()),
654 "grace_period_expired" => {
655 build.grace_period_expired = value.parse::<bool>().ok();
656 }
657 "scan_overdue" => {
658 build.scan_overdue = value.parse::<bool>().ok();
659 }
660 "legacy_scan_engine" => {
661 build.legacy_scan_engine = value.parse::<bool>().ok();
662 }
663 "launch_date" => {
664 if let Ok(date) = NaiveDate::parse_from_str(&value, "%m/%d/%Y") {
665 build.launch_date = Some(date);
666 }
667 }
668 "policy_updated_date" => {
669 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&value) {
670 build.policy_updated_date = Some(datetime.with_timezone(&Utc));
671 }
672 }
673 _ => {
674 build.attributes.insert(key.to_string(), value.to_string());
675 }
676 }
677 }
678 }
679
680 if !build.build_id.is_empty() {
681 build_list.builds.push(build);
682 }
683 }
684 _ => {}
685 }
686 }
687 Ok(Event::Eof) => break,
688 Err(e) => return Err(BuildError::XmlParsingError(e.to_string())),
689 _ => {}
690 }
691 buf.clear();
692 }
693
694 Ok(build_list)
695 }
696
697 fn parse_delete_result(&self, xml: &str) -> Result<DeleteBuildResult, BuildError> {
699 let mut reader = Reader::from_str(xml);
700 reader.config_mut().trim_text(true);
701
702 let mut buf = Vec::new();
703 let mut result = String::new();
704
705 loop {
706 match reader.read_event_into(&mut buf) {
707 Ok(Event::Start(ref e)) => {
708 if e.name().as_ref() == b"result" {
709 match reader.read_event_into(&mut buf) {
711 Ok(Event::Text(e)) => {
712 result = String::from_utf8_lossy(&e).to_string();
713 }
714 _ => {}
715 }
716 }
717 }
718 Ok(Event::Eof) => break,
719 Err(e) => return Err(BuildError::XmlParsingError(e.to_string())),
720 _ => {}
721 }
722 buf.clear();
723 }
724
725 if result.is_empty() {
726 return Err(BuildError::XmlParsingError("No result found in delete response".to_string()));
727 }
728
729 Ok(DeleteBuildResult { result })
730 }
731}
732
733impl BuildApi {
735 pub async fn create_simple_build(&self, app_id: &str, version: Option<&str>) -> Result<Build, BuildError> {
746 let request = CreateBuildRequest {
747 app_id: app_id.to_string(),
748 version: version.map(|s| s.to_string()),
749 lifecycle_stage: None,
750 launch_date: None,
751 sandbox_id: None,
752 };
753
754 self.create_build(request).await
755 }
756
757 pub async fn create_sandbox_build(&self, app_id: &str, sandbox_id: &str, version: Option<&str>) -> Result<Build, BuildError> {
769 let request = CreateBuildRequest {
770 app_id: app_id.to_string(),
771 version: version.map(|s| s.to_string()),
772 lifecycle_stage: None,
773 launch_date: None,
774 sandbox_id: Some(sandbox_id.to_string()),
775 };
776
777 self.create_build(request).await
778 }
779
780 pub async fn delete_app_build(&self, app_id: &str) -> Result<DeleteBuildResult, BuildError> {
790 let request = DeleteBuildRequest {
791 app_id: app_id.to_string(),
792 sandbox_id: None,
793 };
794
795 self.delete_build(request).await
796 }
797
798 pub async fn delete_sandbox_build(&self, app_id: &str, sandbox_id: &str) -> Result<DeleteBuildResult, BuildError> {
809 let request = DeleteBuildRequest {
810 app_id: app_id.to_string(),
811 sandbox_id: Some(sandbox_id.to_string()),
812 };
813
814 self.delete_build(request).await
815 }
816
817 pub async fn get_app_build_info(&self, app_id: &str) -> Result<Build, BuildError> {
827 let request = GetBuildInfoRequest {
828 app_id: app_id.to_string(),
829 build_id: None,
830 sandbox_id: None,
831 };
832
833 self.get_build_info(request).await
834 }
835
836 pub async fn get_sandbox_build_info(&self, app_id: &str, sandbox_id: &str) -> Result<Build, BuildError> {
847 let request = GetBuildInfoRequest {
848 app_id: app_id.to_string(),
849 build_id: None,
850 sandbox_id: Some(sandbox_id.to_string()),
851 };
852
853 self.get_build_info(request).await
854 }
855
856 pub async fn get_app_builds(&self, app_id: &str) -> Result<BuildList, BuildError> {
866 let request = GetBuildListRequest {
867 app_id: app_id.to_string(),
868 sandbox_id: None,
869 };
870
871 self.get_build_list(request).await
872 }
873
874 pub async fn get_sandbox_builds(&self, app_id: &str, sandbox_id: &str) -> Result<BuildList, BuildError> {
885 let request = GetBuildListRequest {
886 app_id: app_id.to_string(),
887 sandbox_id: Some(sandbox_id.to_string()),
888 };
889
890 self.get_build_list(request).await
891 }
892}
893
894#[cfg(test)]
895mod tests {
896 use super::*;
897 use crate::VeracodeConfig;
898
899 #[test]
900 fn test_create_build_request() {
901 let request = CreateBuildRequest {
902 app_id: "123".to_string(),
903 version: Some("1.0.0".to_string()),
904 lifecycle_stage: Some("Development".to_string()),
905 launch_date: Some("12/31/2024".to_string()),
906 sandbox_id: None,
907 };
908
909 assert_eq!(request.app_id, "123");
910 assert_eq!(request.version, Some("1.0.0".to_string()));
911 assert_eq!(request.lifecycle_stage, Some("Development".to_string()));
912 }
913
914 #[test]
915 fn test_update_build_request() {
916 let request = UpdateBuildRequest {
917 app_id: "123".to_string(),
918 build_id: Some("456".to_string()),
919 version: Some("1.1.0".to_string()),
920 lifecycle_stage: Some("QA".to_string()),
921 launch_date: None,
922 sandbox_id: Some("789".to_string()),
923 };
924
925 assert_eq!(request.app_id, "123");
926 assert_eq!(request.build_id, Some("456".to_string()));
927 assert_eq!(request.sandbox_id, Some("789".to_string()));
928 }
929
930 #[test]
931 fn test_build_error_display() {
932 let error = BuildError::BuildNotFound;
933 assert_eq!(error.to_string(), "Build not found");
934
935 let error = BuildError::InvalidParameter("Invalid app_id".to_string());
936 assert_eq!(error.to_string(), "Invalid parameter: Invalid app_id");
937
938 let error = BuildError::CreationFailed("Build creation failed".to_string());
939 assert_eq!(error.to_string(), "Build creation failed: Build creation failed");
940 }
941
942 #[tokio::test]
943 async fn test_build_api_method_signatures() {
944 async fn _test_build_methods() -> Result<(), Box<dyn std::error::Error>> {
945 let config = VeracodeConfig::new("test".to_string(), "test".to_string());
946 let client = VeracodeClient::new(config)?;
947 let api = client.build_api();
948
949 let create_request = CreateBuildRequest {
951 app_id: "123".to_string(),
952 version: None,
953 lifecycle_stage: None,
954 launch_date: None,
955 sandbox_id: None,
956 };
957
958 let _: Result<Build, _> = api.create_build(create_request).await;
961 let _: Result<Build, _> = api.create_simple_build("123", None).await;
962 let _: Result<Build, _> = api.create_sandbox_build("123", "456", None).await;
963 let _: Result<DeleteBuildResult, _> = api.delete_app_build("123").await;
964 let _: Result<Build, _> = api.get_app_build_info("123").await;
965 let _: Result<BuildList, _> = api.get_app_builds("123").await;
966
967 Ok(())
968 }
969
970 assert!(true);
972 }
973}