@@ -1585,45 +1585,78 @@ func TestMain(m *testing.M) {
15851585}
15861586
15871587func TestExecutorAutostartSkipsWhenNoProvisionersAvailable (t * testing.T ) {
1588- t .Parallel ()
1589-
1590- var (
1591- sched = mustSchedule (t ,"CRON_TZ=UTC 0 * * * *" )
1592- tickCh = make (chan time.Time )
1593- statsCh = make (chan autobuild.Stats )
1594- )
1595-
1596- // Create client with provisioner daemon
1597- client ,provisionerCloser := coderdtest .NewWithProvisionerCloser (t ,& coderdtest.Options {
1598- AutobuildTicker :tickCh ,
1599- IncludeProvisionerDaemon :true ,
1600- AutobuildStats :statsCh ,
1601- })
1602-
1603- // Create workspace with autostart enabled
1604- workspace := mustProvisionWorkspace (t ,client ,func (cwr * codersdk.CreateWorkspaceRequest ) {
1605- cwr .AutostartSchedule = ptr .Ref (sched .String ())
1606- })
1607-
1608- // Stop the workspace while provisioner is available
1609- workspace = coderdtest .MustTransitionWorkspace (t ,client ,workspace .ID ,
1610- codersdk .WorkspaceTransitionStart ,codersdk .WorkspaceTransitionStop )
1611-
1612- // Now shut down the provisioner daemon
1613- err := provisionerCloser .Close ()
1614- require .NoError (t ,err )
1615-
1616- // Trigger autobuild after scheduled time
1617- go func () {
1618- tickCh <- sched .Next (workspace .LatestBuild .CreatedAt )
1619- close (tickCh )
1620- }()
1621-
1622- // Wait for executor to run
1623- stats := <- statsCh
1624- require .Len (t ,stats .Errors ,0 ,"should not have errors" )
1625-
1626- // Verify workspace is still stopped
1627- workspace = coderdtest .MustWorkspace (t ,client ,workspace .ID )
1628- require .Equal (t ,codersdk .WorkspaceTransitionStop ,workspace .LatestBuild .Transition )
1588+ t .Parallel ()
1589+
1590+ db ,ps := dbtestutil .NewDB (t )
1591+ var (
1592+ sched = mustSchedule (t ,"CRON_TZ=UTC 0 * * * *" )
1593+ tickCh = make (chan time.Time )
1594+ statsCh = make (chan autobuild.Stats )
1595+ )
1596+
1597+ // Create client with provisioner closer
1598+ client ,provisionerCloser := coderdtest .NewWithProvisionerCloser (t ,& coderdtest.Options {
1599+ AutobuildTicker :tickCh ,
1600+ IncludeProvisionerDaemon :true ,
1601+ AutobuildStats :statsCh ,
1602+ Database :db ,
1603+ Pubsub :ps ,
1604+ SkipProvisionerCheck :ptr .Ref (false ),
1605+ })
1606+
1607+ // Create workspace with autostart enabled
1608+ workspace := mustProvisionWorkspace (t ,client ,func (cwr * codersdk.CreateWorkspaceRequest ) {
1609+ cwr .AutostartSchedule = ptr .Ref (sched .String ())
1610+ })
1611+
1612+ // Stop the workspace while provisioner is available
1613+ workspace = coderdtest .MustTransitionWorkspace (t ,client ,workspace .ID ,
1614+ codersdk .WorkspaceTransitionStart ,codersdk .WorkspaceTransitionStop )
1615+
1616+ // Wait for provisioner to be registered
1617+ ctx := testutil .Context (t ,testutil .WaitShort )
1618+ require .Eventually (t ,func ()bool {
1619+ daemons ,err := db .GetProvisionerDaemons (ctx )
1620+ return err == nil && len (daemons )> 0
1621+ },testutil .WaitShort ,testutil .IntervalFast )
1622+
1623+ // Now shut down the provisioner daemon
1624+ err := provisionerCloser .Close ()
1625+ require .NoError (t ,err )
1626+
1627+ // Debug: check what's in the database
1628+ daemons ,err := db .GetProvisionerDaemons (ctx )
1629+ require .NoError (t ,err )
1630+ t .Logf ("After close: found %d daemons" ,len (daemons ))
1631+ for i ,daemon := range daemons {
1632+ t .Logf ("Daemon %d: ID=%s, Name=%s, LastSeen=%v" ,i ,daemon .ID ,daemon .Name ,daemon .LastSeenAt )
1633+ }
1634+
1635+ // Wait for provisioner to become stale (LastSeenAt > StaleInterval ago)
1636+ require .Eventually (t ,func ()bool {
1637+ daemons ,err := db .GetProvisionerDaemons (ctx )
1638+ if err != nil || len (daemons )== 0 {
1639+ return false
1640+ }
1641+
1642+ now := time .Now ()
1643+ for _ ,daemon := range daemons {
1644+ if daemon .LastSeenAt .Valid {
1645+ age := now .Sub (daemon .LastSeenAt .Time )
1646+ if age > autobuild .TestingStaleInterval {
1647+ return true // Provisioner is now stale
1648+ }
1649+ }
1650+ }
1651+ return false
1652+ },testutil .WaitLong ,testutil .IntervalMedium )// Use longer timeout since we need to wait for staleness
1653+
1654+ // Trigger autobuild
1655+ go func () {
1656+ tickCh <- sched .Next (workspace .LatestBuild .CreatedAt )
1657+ close (tickCh )
1658+ }()
1659+
1660+ stats := <- statsCh
1661+ assert .Len (t ,stats .Transitions ,0 ,"should not create builds when no provisioners available" )
16291662}