@@ -1564,6 +1564,69 @@ func TestCompleteJob(t *testing.T) {
1564
1564
require .Contains (t , job .Error .String , `external auth provider "github" is not configured` )
1565
1565
})
1566
1566
1567
+ t .Run ("TemplateImport_WithAITasks" , func (t * testing.T ) {
1568
+ t .Parallel ()
1569
+
1570
+ t .Run ("WithoutPromptParam" , func (t * testing.T ) {
1571
+ t .Parallel ()
1572
+
1573
+ srv , db , _ , pd := setup (t , false , & overrides {})
1574
+ jobID := uuid .New ()
1575
+ versionID := uuid .New ()
1576
+ err := db .InsertTemplateVersion (ctx , database.InsertTemplateVersionParams {
1577
+ ID : versionID ,
1578
+ JobID : jobID ,
1579
+ OrganizationID : pd .OrganizationID ,
1580
+ })
1581
+ require .NoError (t , err )
1582
+ job , err := db .InsertProvisionerJob (ctx , database.InsertProvisionerJobParams {
1583
+ ID : jobID ,
1584
+ Provisioner : database .ProvisionerTypeEcho ,
1585
+ Input : []byte (`{"template_version_id": "` + versionID .String () + `"}` ),
1586
+ StorageMethod : database .ProvisionerStorageMethodFile ,
1587
+ Type : database .ProvisionerJobTypeWorkspaceBuild ,
1588
+ OrganizationID : pd .OrganizationID ,
1589
+ Tags : pd .Tags ,
1590
+ })
1591
+ require .NoError (t , err )
1592
+ _ , err = db .AcquireProvisionerJob (ctx , database.AcquireProvisionerJobParams {
1593
+ OrganizationID : pd .OrganizationID ,
1594
+ WorkerID : uuid.NullUUID {
1595
+ UUID : pd .ID ,
1596
+ Valid : true ,
1597
+ },
1598
+ Types : []database.ProvisionerType {database .ProvisionerTypeEcho },
1599
+ StartedAt : sql.NullTime {
1600
+ Time : dbtime .Now (),
1601
+ Valid : true ,
1602
+ },
1603
+ ProvisionerTags : must (json .Marshal (job .Tags )),
1604
+ })
1605
+ require .NoError (t , err )
1606
+ completeJob := func () {
1607
+ _ , err = srv .CompleteJob (ctx , & proto.CompletedJob {
1608
+ JobId : job .ID .String (),
1609
+ Type : & proto.CompletedJob_TemplateImport_ {
1610
+ TemplateImport : & proto.CompletedJob_TemplateImport {
1611
+ StartResources : []* sdkproto.Resource {{
1612
+ Name : "my_task" ,
1613
+ Type : "coder_ai_task" ,
1614
+ }},
1615
+ StopResources : []* sdkproto.Resource {},
1616
+ HasAiTasks : true ,
1617
+ Plan : []byte ("{}" ),
1618
+ },
1619
+ },
1620
+ })
1621
+ require .NoError (t , err )
1622
+ }
1623
+ completeJob ()
1624
+ job , err = db .GetProvisionerJobByID (ctx , job .ID )
1625
+ require .NoError (t , err )
1626
+ require .Contains (t , job .Error .String , `external auth provider "github" is not configured` )
1627
+ })
1628
+ })
1629
+
1567
1630
t .Run ("TemplateImport_WithGitAuth" , func (t * testing.T ) {
1568
1631
t .Parallel ()
1569
1632
srv , db , _ , pd := setup (t , false , & overrides {
@@ -2435,6 +2498,233 @@ func TestCompleteJob(t *testing.T) {
2435
2498
testutil .RequireReceive (ctx , t , done )
2436
2499
require .Equal (t , replacements , orchestrator .replacements )
2437
2500
})
2501
+
2502
+ t .Run ("AITasks" , func (t * testing.T ) {
2503
+ t .Parallel ()
2504
+
2505
+ // has_ai_task has a default value of nil, but once the template import completes it will have a value;
2506
+ // it is set to "true" if the template has any coder_ai_task resources defined.
2507
+ t .Run ("TemplateImport" , func (t * testing.T ) {
2508
+ type testcase struct {
2509
+ name string
2510
+ input * proto.CompletedJob_TemplateImport
2511
+ expected bool
2512
+ }
2513
+
2514
+ for _ , tc := range []testcase {
2515
+ {
2516
+ name : "has_ai_task is false by default" ,
2517
+ input : & proto.CompletedJob_TemplateImport {
2518
+ // HasAiTasks is not set.
2519
+ Plan : []byte ("{}" ),
2520
+ },
2521
+ expected : false ,
2522
+ },
2523
+ {
2524
+ name : "has_ai_task gets set to true" ,
2525
+ input : & proto.CompletedJob_TemplateImport {
2526
+ HasAiTasks : true ,
2527
+ Plan : []byte ("{}" ),
2528
+ },
2529
+ expected : true ,
2530
+ },
2531
+ } {
2532
+ t .Run (tc .name , func (t * testing.T ) {
2533
+ t .Parallel ()
2534
+
2535
+ srv , db , _ , pd := setup (t , false , & overrides {})
2536
+
2537
+ importJobID := uuid .New ()
2538
+ tvID := uuid .New ()
2539
+ template := dbgen .Template (t , db , database.Template {
2540
+ Name : "template" ,
2541
+ Provisioner : database .ProvisionerTypeEcho ,
2542
+ OrganizationID : pd .OrganizationID ,
2543
+ })
2544
+ version := dbgen .TemplateVersion (t , db , database.TemplateVersion {
2545
+ ID : tvID ,
2546
+ OrganizationID : pd .OrganizationID ,
2547
+ TemplateID : uuid.NullUUID {
2548
+ UUID : template .ID ,
2549
+ Valid : true ,
2550
+ },
2551
+ JobID : importJobID ,
2552
+ })
2553
+ _ = version
2554
+
2555
+ ctx := testutil .Context (t , testutil .WaitShort )
2556
+ job , err := db .InsertProvisionerJob (ctx , database.InsertProvisionerJobParams {
2557
+ ID : importJobID ,
2558
+ CreatedAt : dbtime .Now (),
2559
+ UpdatedAt : dbtime .Now (),
2560
+ OrganizationID : pd .OrganizationID ,
2561
+ InitiatorID : uuid .New (),
2562
+ Input : must (json .Marshal (provisionerdserver.TemplateVersionImportJob {
2563
+ TemplateVersionID : tvID ,
2564
+ })),
2565
+ Provisioner : database .ProvisionerTypeEcho ,
2566
+ StorageMethod : database .ProvisionerStorageMethodFile ,
2567
+ Type : database .ProvisionerJobTypeTemplateVersionImport ,
2568
+ Tags : pd .Tags ,
2569
+ })
2570
+ require .NoError (t , err )
2571
+
2572
+ _ , err = db .AcquireProvisionerJob (ctx , database.AcquireProvisionerJobParams {
2573
+ OrganizationID : pd .OrganizationID ,
2574
+ WorkerID : uuid.NullUUID {
2575
+ UUID : pd .ID ,
2576
+ Valid : true ,
2577
+ },
2578
+ Types : []database.ProvisionerType {database .ProvisionerTypeEcho },
2579
+ ProvisionerTags : must (json .Marshal (job .Tags )),
2580
+ StartedAt : sql.NullTime {Time : job .CreatedAt , Valid : true },
2581
+ })
2582
+ require .NoError (t , err )
2583
+
2584
+ version , err = db .GetTemplateVersionByID (ctx , tvID )
2585
+ require .NoError (t , err )
2586
+ require .False (t , version .HasAITask .Valid ) // Value should be nil (i.e. valid = false).
2587
+
2588
+ completedJob := proto.CompletedJob {
2589
+ JobId : job .ID .String (),
2590
+ Type : & proto.CompletedJob_TemplateImport_ {
2591
+ TemplateImport : tc .input ,
2592
+ },
2593
+ }
2594
+ _ , err = srv .CompleteJob (ctx , & completedJob )
2595
+ require .NoError (t , err )
2596
+
2597
+ version , err = db .GetTemplateVersionByID (ctx , tvID )
2598
+ require .NoError (t , err )
2599
+ require .True (t , version .HasAITask .Valid ) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
2600
+ require .Equal (t , tc .expected , version .HasAITask .Bool )
2601
+ })
2602
+ }
2603
+ })
2604
+
2605
+ // has_ai_task has a default value of nil, but once the workspace build completes it will have a value;
2606
+ // it is set to "true" if the related template has any coder_ai_task resources defined, and its sidebar app ID
2607
+ // will be set as well in that case.
2608
+ t .Run ("WorkspaceBuild" , func (t * testing.T ) {
2609
+ type testcase struct {
2610
+ name string
2611
+ input * proto.CompletedJob_WorkspaceBuild
2612
+ expected bool
2613
+ }
2614
+
2615
+ sidebarAppID := uuid .NewString ()
2616
+ for _ , tc := range []testcase {
2617
+ {
2618
+ name : "has_ai_task is false by default" ,
2619
+ input : & proto.CompletedJob_WorkspaceBuild {
2620
+ // No AiTasks defined.
2621
+ },
2622
+ expected : false ,
2623
+ },
2624
+ {
2625
+ name : "has_ai_task is set to true" ,
2626
+ input : & proto.CompletedJob_WorkspaceBuild {
2627
+ AiTasks : []* sdkproto.AITask {
2628
+ {
2629
+ Id : uuid .NewString (),
2630
+ SidebarApp : & sdkproto.AITaskSidebarApp {
2631
+ Id : sidebarAppID ,
2632
+ },
2633
+ },
2634
+ },
2635
+ },
2636
+ expected : true ,
2637
+ },
2638
+ } {
2639
+ t .Run (tc .name , func (t * testing.T ) {
2640
+ t .Parallel ()
2641
+
2642
+ srv , db , _ , pd := setup (t , false , & overrides {})
2643
+
2644
+ importJobID := uuid .New ()
2645
+ tvID := uuid .New ()
2646
+ template := dbgen .Template (t , db , database.Template {
2647
+ Name : "template" ,
2648
+ Provisioner : database .ProvisionerTypeEcho ,
2649
+ OrganizationID : pd .OrganizationID ,
2650
+ })
2651
+ version := dbgen .TemplateVersion (t , db , database.TemplateVersion {
2652
+ ID : tvID ,
2653
+ OrganizationID : pd .OrganizationID ,
2654
+ TemplateID : uuid.NullUUID {
2655
+ UUID : template .ID ,
2656
+ Valid : true ,
2657
+ },
2658
+ JobID : importJobID ,
2659
+ })
2660
+ user := dbgen .User (t , db , database.User {})
2661
+ workspaceTable := dbgen .Workspace (t , db , database.WorkspaceTable {
2662
+ TemplateID : template .ID ,
2663
+ OwnerID : user .ID ,
2664
+ OrganizationID : pd .OrganizationID ,
2665
+ })
2666
+ build := dbgen .WorkspaceBuild (t , db , database.WorkspaceBuild {
2667
+ WorkspaceID : workspaceTable .ID ,
2668
+ TemplateVersionID : version .ID ,
2669
+ InitiatorID : user .ID ,
2670
+ Transition : database .WorkspaceTransitionStart ,
2671
+ })
2672
+
2673
+ ctx := testutil .Context (t , testutil .WaitShort )
2674
+ job , err := db .InsertProvisionerJob (ctx , database.InsertProvisionerJobParams {
2675
+ ID : importJobID ,
2676
+ CreatedAt : dbtime .Now (),
2677
+ UpdatedAt : dbtime .Now (),
2678
+ OrganizationID : pd .OrganizationID ,
2679
+ InitiatorID : uuid .New (),
2680
+ Input : must (json .Marshal (provisionerdserver.WorkspaceProvisionJob {
2681
+ WorkspaceBuildID : build .ID ,
2682
+ LogLevel : "DEBUG" ,
2683
+ })),
2684
+ Provisioner : database .ProvisionerTypeEcho ,
2685
+ StorageMethod : database .ProvisionerStorageMethodFile ,
2686
+ Type : database .ProvisionerJobTypeWorkspaceBuild ,
2687
+ Tags : pd .Tags ,
2688
+ })
2689
+ require .NoError (t , err )
2690
+
2691
+ _ , err = db .AcquireProvisionerJob (ctx , database.AcquireProvisionerJobParams {
2692
+ OrganizationID : pd .OrganizationID ,
2693
+ WorkerID : uuid.NullUUID {
2694
+ UUID : pd .ID ,
2695
+ Valid : true ,
2696
+ },
2697
+ Types : []database.ProvisionerType {database .ProvisionerTypeEcho },
2698
+ ProvisionerTags : must (json .Marshal (job .Tags )),
2699
+ StartedAt : sql.NullTime {Time : job .CreatedAt , Valid : true },
2700
+ })
2701
+ require .NoError (t , err )
2702
+
2703
+ build , err = db .GetWorkspaceBuildByID (ctx , build .ID )
2704
+ require .NoError (t , err )
2705
+ require .False (t , build .HasAITask .Valid ) // Value should be nil (i.e. valid = false).
2706
+
2707
+ completedJob := proto.CompletedJob {
2708
+ JobId : job .ID .String (),
2709
+ Type : & proto.CompletedJob_WorkspaceBuild_ {
2710
+ WorkspaceBuild : tc .input ,
2711
+ },
2712
+ }
2713
+ _ , err = srv .CompleteJob (ctx , & completedJob )
2714
+ require .NoError (t , err )
2715
+
2716
+ build , err = db .GetWorkspaceBuildByID (ctx , build .ID )
2717
+ require .NoError (t , err )
2718
+ require .True (t , build .HasAITask .Valid ) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
2719
+ require .Equal (t , tc .expected , build .HasAITask .Bool )
2720
+
2721
+ if tc .expected {
2722
+ require .Equal (t , sidebarAppID , build .AITaskSidebarAppID .UUID .String ())
2723
+ }
2724
+ })
2725
+ }
2726
+ })
2727
+ })
2438
2728
}
2439
2729
2440
2730
type mockPrebuildsOrchestrator struct {
0 commit comments