@@ -34,8 +34,10 @@ import (
3434 "github.com/riverqueue/river/riverdriver"
3535 "github.com/riverqueue/river/riverdriver/riverpgxv5"
3636 "github.com/riverqueue/river/rivershared/baseservice"
37+ "github.com/riverqueue/river/rivershared/riverpilot"
3738 "github.com/riverqueue/river/rivershared/riversharedmaintenance"
3839 "github.com/riverqueue/river/rivershared/riversharedtest"
40+ "github.com/riverqueue/river/rivershared/startstop"
3941 "github.com/riverqueue/river/rivershared/startstoptest"
4042 "github.com/riverqueue/river/rivershared/testfactory"
4143 "github.com/riverqueue/river/rivershared/util/dbutil"
@@ -5173,6 +5175,67 @@ func Test_Client_Maintenance(t *testing.T) {
51735175 require .Len (t , jobs , 1 , "Expected to find exactly one job of kind: " + (periodicJobArgs {}).Kind ())
51745176 })
51755177
5178+ t .Run ("PeriodicJobEnqueuerUnknownConfigureFromPilotNilResult" , func (t * testing.T ) {
5179+ t .Parallel ()
5180+
5181+ var (
5182+ dbPool = riversharedtest .DBPool (ctx , t )
5183+ config = newTestConfig (t , "" )
5184+ pluginDriver = newDriverWithPlugin (t , dbPool )
5185+ pluginPilot = & TestPilotWithUnknownConfigure {}
5186+ )
5187+ pluginDriver .pilot = pluginPilot
5188+
5189+ var unknownJobConfigureCalled bool
5190+ pluginPilot .PeriodicJobUnknownConfigureFunc = func (job * riverpilot.PeriodicJob ) * riverpilot.UnknownConfigureResult {
5191+ unknownJobConfigureCalled = true
5192+ return nil
5193+ }
5194+
5195+ client , err := NewClient (pluginDriver , config )
5196+ require .NoError (t , err )
5197+
5198+ svc := maintenance.GetService [* maintenance.PeriodicJobEnqueuer ](client .queueMaintainer )
5199+ svc .Config .UnknownConfigure (& riverpilot.PeriodicJob {})
5200+ require .True (t , unknownJobConfigureCalled )
5201+ })
5202+
5203+ t .Run ("PeriodicJobEnqueuerUnknownConfigureFromPilotNonNilResult" , func (t * testing.T ) {
5204+ t .Parallel ()
5205+
5206+ var (
5207+ dbPool = riversharedtest .DBPool (ctx , t )
5208+ config = newTestConfig (t , "" )
5209+ pluginDriver = newDriverWithPlugin (t , dbPool )
5210+ pluginPilot = & TestPilotWithUnknownConfigure {}
5211+ )
5212+ pluginDriver .pilot = pluginPilot
5213+
5214+ var (
5215+ jobConstructorCalled bool
5216+ unknownJobConfigureCalled bool
5217+ )
5218+ pluginPilot .PeriodicJobUnknownConfigureFunc = func (job * riverpilot.PeriodicJob ) * riverpilot.UnknownConfigureResult {
5219+ unknownJobConfigureCalled = true
5220+ return & riverpilot.UnknownConfigureResult {
5221+ JobConstructor : func () (rivertype.JobArgs , * rivertype.InsertOpts ) {
5222+ jobConstructorCalled = true
5223+ return & noOpArgs {}, & rivertype.InsertOpts {}
5224+ },
5225+ Schedule : cron .Every (time .Minute ),
5226+ }
5227+ }
5228+
5229+ client , err := NewClient (pluginDriver , config )
5230+ require .NoError (t , err )
5231+
5232+ svc := maintenance.GetService [* maintenance.PeriodicJobEnqueuer ](client .queueMaintainer )
5233+ unknownConfigureRes := svc .Config .UnknownConfigure (& riverpilot.PeriodicJob {})
5234+ require .True (t , unknownJobConfigureCalled )
5235+ unknownConfigureRes .JobConstructor ()
5236+ require .True (t , jobConstructorCalled )
5237+ })
5238+
51765239 t .Run ("QueueCleaner" , func (t * testing.T ) {
51775240 t .Parallel ()
51785241
@@ -8173,3 +8236,96 @@ func (f JobArgsWithHooksFunc) Hooks() []rivertype.Hook {
81738236func (JobArgsWithHooksFunc ) MarshalJSON () ([]byte , error ) { return []byte ("{}" ), nil }
81748237
81758238func (JobArgsWithHooksFunc ) UnmarshalJSON ([]byte ) error { return nil }
8239+
8240+ var _ pilotPlugin = & TestPilotWithUnknownConfigure {}
8241+
8242+ type TestPilotWithUnknownConfigure struct {
8243+ riverpilot.StandardPilot
8244+ PeriodicJobUnknownConfigureFunc func (job * riverpilot.PeriodicJob ) * riverpilot.UnknownConfigureResult
8245+ }
8246+
8247+ func (p * TestPilotWithUnknownConfigure ) PeriodicJobUnknownConfigure () func (job * riverpilot.PeriodicJob ) * riverpilot.UnknownConfigureResult {
8248+ return p .PeriodicJobUnknownConfigureFunc
8249+ }
8250+
8251+ func (p * TestPilotWithUnknownConfigure ) PluginServices () []startstop.Service { return nil }
8252+
8253+ func (p * TestPilotWithUnknownConfigure ) PluginMaintenanceServices () []startstop.Service { return nil }
8254+
8255+ func TestTagRE (t * testing.T ) {
8256+ t .Parallel ()
8257+
8258+ require .Regexp (t , tagRE , "aaa" )
8259+ require .Regexp (t , tagRE , "_aaa" )
8260+ require .Regexp (t , tagRE , "aaa_" )
8261+ require .Regexp (t , tagRE , "777" )
8262+ require .Regexp (t , tagRE , "my-tag" )
8263+ require .Regexp (t , tagRE , "my_tag" )
8264+ require .Regexp (t , tagRE , "my-longer-tag" )
8265+ require .Regexp (t , tagRE , "my_longer_tag" )
8266+ require .Regexp (t , tagRE , "My_Capitalized_Tag" )
8267+ require .Regexp (t , tagRE , "ALL_CAPS" )
8268+ require .Regexp (t , tagRE , "1_2_3" )
8269+
8270+ require .NotRegexp (t , tagRE , "a" )
8271+ require .NotRegexp (t , tagRE , "aa" )
8272+ require .NotRegexp (t , tagRE , "-aaa" )
8273+ require .NotRegexp (t , tagRE , "aaa-" )
8274+ require .NotRegexp (t , tagRE , "special@characters$banned" )
8275+ require .NotRegexp (t , tagRE , "commas,never,allowed" )
8276+ }
8277+
8278+ func TestUniqueOptsIsEmpty (t * testing.T ) {
8279+ t .Parallel ()
8280+
8281+ require .True (t , uniqueOptsIsEmpty (& UniqueOpts {}))
8282+ require .False (t , uniqueOptsIsEmpty (& UniqueOpts {ByArgs : true }))
8283+ require .False (t , uniqueOptsIsEmpty (& UniqueOpts {ByPeriod : 1 * time .Nanosecond }))
8284+ require .False (t , uniqueOptsIsEmpty (& UniqueOpts {ByQueue : true }))
8285+ require .False (t , uniqueOptsIsEmpty (& UniqueOpts {ByState : []rivertype.JobState {rivertype .JobStateAvailable }}))
8286+ }
8287+
8288+ func TestUniqueOptsValidate (t * testing.T ) {
8289+ t .Parallel ()
8290+
8291+ require .NoError (t , uniqueOptsValidate (& UniqueOpts {}))
8292+ require .NoError (t , uniqueOptsValidate (& UniqueOpts {
8293+ ByArgs : true ,
8294+ ByPeriod : 1 * time .Second ,
8295+ ByQueue : true ,
8296+ }))
8297+
8298+ require .EqualError (t , uniqueOptsValidate (& UniqueOpts {ByPeriod : 1 * time .Millisecond }), "UniqueOpts.ByPeriod should not be less than 1 second" )
8299+ require .EqualError (t , uniqueOptsValidate (& UniqueOpts {ByState : []rivertype.JobState {rivertype .JobState ("invalid" )}}), `UniqueOpts.ByState contains invalid state "invalid"` )
8300+
8301+ requiredStates := []rivertype.JobState {
8302+ rivertype .JobStateAvailable ,
8303+ rivertype .JobStatePending ,
8304+ rivertype .JobStateRunning ,
8305+ rivertype .JobStateScheduled ,
8306+ }
8307+
8308+ for _ , state := range requiredStates {
8309+ // Test with each state individually removed from requiredStates to ensure
8310+ // it's validated.
8311+
8312+ // Create a copy of requiredStates without the current state
8313+ var testStates []rivertype.JobState
8314+ for _ , s := range requiredStates {
8315+ if s != state {
8316+ testStates = append (testStates , s )
8317+ }
8318+ }
8319+
8320+ // Test validation
8321+ require .EqualError (t , uniqueOptsValidate (& UniqueOpts {ByState : testStates }), "UniqueOpts.ByState must contain all required states, missing: " + string (state ))
8322+ }
8323+
8324+ // test with more than one required state missing:
8325+ require .EqualError (t , uniqueOptsValidate (& UniqueOpts {ByState : []rivertype.JobState {
8326+ rivertype .JobStateAvailable ,
8327+ rivertype .JobStateScheduled ,
8328+ }}), "UniqueOpts.ByState must contain all required states, missing: pending, running" )
8329+
8330+ require .NoError (t , uniqueOptsValidate (& UniqueOpts {ByState : rivertype .JobStates ()}))
8331+ }
0 commit comments