Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(quartz): organize package error types #94

Merged
merged 1 commit into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@ package assert

import (
"reflect"
"strings"
"testing"
)

// Equal verifies equality of two objects.
func Equal[T any](t *testing.T, a T, b T) {
if !reflect.DeepEqual(a, b) {
t.Fatalf("%v != %v", a, b)
}
}

// NotEqual verifies objects are not equal.
func NotEqual[T any](t *testing.T, a T, b T) {
if reflect.DeepEqual(a, b) {
t.Fatalf("%v == %v", a, b)
}
}

// ErrorContains checks whether the given error contains the specified string.
func ErrorContains(t *testing.T, err error, str string) {
if err == nil {
t.Fatalf("Error is nil")
} else if !strings.Contains(err.Error(), str) {
t.Fatalf("Error doen't contain string: %s", str)
}
}
31 changes: 15 additions & 16 deletions quartz/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func NewCronTrigger(expression string) (*CronTrigger, error) {
// NewCronTriggerWithLoc returns a new CronTrigger with the given time.Location.
func NewCronTriggerWithLoc(expression string, location *time.Location) (*CronTrigger, error) {
if location == nil {
return nil, errors.New("location is nil")
return nil, illegalArgumentError("location is nil")
}

fields, err := validateCronExpression(expression)
Expand All @@ -62,7 +62,7 @@ func NewCronTriggerWithLoc(expression string, location *time.Location) (*CronTri

// full wildcard expression
if lastDefined == -1 {
fields[0].values, _ = fillRange(0, 59)
fields[0].values, _ = fillRangeValues(0, 59)
}

return &CronTrigger{
Expand Down Expand Up @@ -158,13 +158,13 @@ func validateCronExpression(expression string) ([]*cronField, error) {
}
length := len(tokens)
if length < 6 || length > 7 {
return nil, cronError("invalid expression length")
return nil, cronParseError("invalid expression length")
}
if length == 6 {
tokens = append(tokens, "*")
}
if (tokens[3] != "?" && tokens[3] != "*") && (tokens[5] != "?" && tokens[5] != "*") {
return nil, cronError("day field was set twice")
return nil, cronParseError("day field set twice")
}

return buildCronField(tokens)
Expand Down Expand Up @@ -223,13 +223,13 @@ func parseField(field string, min, max int, translate ...[]string) (*cronField,
return &cronField{[]int{}}, nil
}

// single value
// simple value
i, err := strconv.Atoi(field)
if err == nil {
if inScope(i, min, max) {
return &cronField{[]int{i}}, nil
}
return nil, cronError("single min/max validation error")
return nil, cronParseError("simple field min/max validation")
}

// list values
Expand All @@ -247,18 +247,18 @@ func parseField(field string, min, max int, translate ...[]string) (*cronField,
return parseStepField(field, min, max, dict)
}

// literal single value
// simple literal value
if dict != nil {
i := intVal(dict, field)
if i >= 0 {
if inScope(i, min, max) {
return &cronField{[]int{i}}, nil
}
return nil, cronError("cron literal min/max validation error")
return nil, cronParseError("simple literal min/max validation")
}
}

return nil, cronError("cron parse error")
return nil, cronParseError("parse error")
}

func parseListField(field string, min, max int, translate []string) (*cronField, error) {
Expand Down Expand Up @@ -286,17 +286,16 @@ func parseListField(field string, min, max int, translate []string) (*cronField,
func parseRangeField(field string, min, max int, translate []string) (*cronField, error) {
t := strings.Split(field, "-")
if len(t) != 2 {
return nil, cronError("parse cron range error")
return nil, cronParseError(fmt.Sprintf("invalid range field %s", field))
}

from := normalize(t[0], translate)
to := normalize(t[1], translate)
if !inScope(from, min, max) || !inScope(to, min, max) {
return nil, cronError(fmt.Sprintf("cron range min/max validation error %d-%d",
from, to))
return nil, cronParseError(fmt.Sprintf("range field min/max validation %d-%d", from, to))
}

rangeValues, err := fillRange(from, to)
rangeValues, err := fillRangeValues(from, to)
if err != nil {
return nil, err
}
Expand All @@ -307,7 +306,7 @@ func parseRangeField(field string, min, max int, translate []string) (*cronField
func parseStepField(field string, min, max int, translate []string) (*cronField, error) {
t := strings.Split(field, "/")
if len(t) != 2 {
return nil, cronError("parse cron step error")
return nil, cronParseError(fmt.Sprintf("invalid step field %s", field))
}

if t[0] == "*" {
Expand All @@ -317,10 +316,10 @@ func parseStepField(field string, min, max int, translate []string) (*cronField,
from := normalize(t[0], translate)
step := atoi(t[1])
if !inScope(from, min, max) {
return nil, cronError("cron step min/max validation error")
return nil, cronParseError("step field min/max validation")
}

stepValues, err := fillStep(from, step, max)
stepValues, err := fillStepValues(from, step, max)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions quartz/cron_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,12 @@ func TestCronHourly(t *testing.T) {

func TestCronExpressionInvalidLength(t *testing.T) {
_, err := quartz.NewCronTrigger("0 0 0 * *")
assert.Equal(t, err.Error(), "invalid cron expression: invalid expression length")
assert.ErrorContains(t, err, "invalid expression length")
}

func TestCronTriggerNilLocationError(t *testing.T) {
_, err := quartz.NewCronTriggerWithLoc("@daily", nil)
assert.Equal(t, err.Error(), "location is nil")
assert.ErrorContains(t, err, "location is nil")
}

func TestCronExpressionDescription(t *testing.T) {
Expand Down
31 changes: 31 additions & 0 deletions quartz/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package quartz

import (
"errors"
"fmt"
)

// Errors
var (
ErrIllegalArgument = errors.New("illegal argument")
ErrCronParse = errors.New("parse cron expression")
ErrJobNotFound = errors.New("job not found")
)

// illegalArgumentError returns an illegal argument error with a custom
// error message, which unwraps to ErrIllegalArgument.
func illegalArgumentError(message string) error {
return fmt.Errorf("%w: %s", ErrIllegalArgument, message)
}

// cronParseError returns a cron parse error with a custom error message,
// which unwraps to ErrCronParse.
func cronParseError(message string) error {
return fmt.Errorf("%w: %s", ErrCronParse, message)
}

// jobNotFoundError returns a job not found error with a custom error message,
// which unwraps to ErrJobNotFound.
func jobNotFoundError(message string) error {
return fmt.Errorf("%w: %s", ErrJobNotFound, message)
}
36 changes: 36 additions & 0 deletions quartz/error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package quartz

import (
"errors"
"fmt"
"testing"

"github.com/reugn/go-quartz/internal/assert"
)

func TestIllegalArgumentError(t *testing.T) {
message := "argument is nil"
err := illegalArgumentError(message)
if !errors.Is(err, ErrIllegalArgument) {
t.Fatal("error must match ErrIllegalArgument")
}
assert.Equal(t, err.Error(), fmt.Sprintf("%s: %s", ErrIllegalArgument, message))
}

func TestCronParseError(t *testing.T) {
message := "invalid field"
err := cronParseError(message)
if !errors.Is(err, ErrCronParse) {
t.Fatal("error must match ErrCronParse")
}
assert.Equal(t, err.Error(), fmt.Sprintf("%s: %s", ErrCronParse, message))
}

func TestJobNotFoundError(t *testing.T) {
message := "for key"
err := jobNotFoundError(message)
if !errors.Is(err, ErrJobNotFound) {
t.Fatal("error must match ErrJobNotFound")
}
assert.Equal(t, err.Error(), fmt.Sprintf("%s: %s", ErrJobNotFound, message))
}
2 changes: 1 addition & 1 deletion quartz/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (jq *jobQueue) Remove(jobKey *JobKey) (ScheduledJob, error) {
return heap.Remove(&jq.delegate, i).(ScheduledJob), nil
}
}
return nil, errors.New("no job with the given key found")
return nil, jobNotFoundError(fmt.Sprintf("for key %s", jobKey))
}

// ScheduledJobs returns the slice of all scheduled jobs in the queue.
Expand Down
16 changes: 8 additions & 8 deletions quartz/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package quartz

import (
"context"
"errors"
"fmt"
"sync"
"time"

Expand Down Expand Up @@ -136,16 +136,16 @@ func (sched *StdScheduler) ScheduleJob(
trigger Trigger,
) error {
if jobDetail == nil {
return errors.New("jobDetail is nil")
return illegalArgumentError("jobDetail is nil")
}
if jobDetail.jobKey == nil {
return errors.New("jobDetail.jobKey is nil")
return illegalArgumentError("jobDetail.jobKey is nil")
}
if jobDetail.jobKey.name == "" {
return errors.New("empty key name is not allowed")
return illegalArgumentError("empty key name is not allowed")
}
if trigger == nil {
return errors.New("trigger is nil")
return illegalArgumentError("trigger is nil")
}
nextRunTime, err := trigger.NextFireTime(NowNano())
if err != nil {
Expand Down Expand Up @@ -224,21 +224,21 @@ func (sched *StdScheduler) GetJobKeys() []*JobKey {
// GetScheduledJob returns the ScheduledJob with the specified key.
func (sched *StdScheduler) GetScheduledJob(jobKey *JobKey) (ScheduledJob, error) {
if jobKey == nil {
return nil, errors.New("jobKey is nil")
return nil, illegalArgumentError("jobKey is nil")
}
scheduledJobs := sched.queue.ScheduledJobs()
for _, scheduled := range scheduledJobs {
if scheduled.JobDetail().jobKey.Equals(jobKey) {
return scheduled, nil
}
}
return nil, errors.New("no job with the given key found")
return nil, jobNotFoundError(fmt.Sprintf("for key %s", jobKey))
}

// DeleteJob removes the Job with the specified key if present.
func (sched *StdScheduler) DeleteJob(jobKey *JobKey) error {
if jobKey == nil {
return errors.New("jobKey is nil")
return illegalArgumentError("jobKey is nil")
}
_, err := sched.queue.Remove(jobKey)
if err == nil {
Expand Down
16 changes: 8 additions & 8 deletions quartz/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestScheduler(t *testing.T) {

nonExistentJobKey := quartz.NewJobKey("NA")
_, err = sched.GetScheduledJob(nonExistentJobKey)
assert.NotEqual(t, err, nil)
assert.ErrorContains(t, err, quartz.ErrJobNotFound.Error())

err = sched.DeleteJob(nonExistentJobKey)
assert.NotEqual(t, err, nil)
Expand Down Expand Up @@ -381,21 +381,21 @@ func TestSchedulerArgumentValidationErrors(t *testing.T) {
assert.Equal(t, err, nil)

err = sched.ScheduleJob(nil, trigger)
assert.Equal(t, err.Error(), "jobDetail is nil")
assert.ErrorContains(t, err, "jobDetail is nil")
err = sched.ScheduleJob(quartz.NewJobDetail(job, nil), trigger)
assert.Equal(t, err.Error(), "jobDetail.jobKey is nil")
assert.ErrorContains(t, err, "jobDetail.jobKey is nil")
err = sched.ScheduleJob(quartz.NewJobDetail(job, quartz.NewJobKey("")), trigger)
assert.Equal(t, err.Error(), "empty key name is not allowed")
assert.ErrorContains(t, err, "empty key name is not allowed")
err = sched.ScheduleJob(quartz.NewJobDetail(job, quartz.NewJobKeyWithGroup("job", "")), nil)
assert.Equal(t, err.Error(), "trigger is nil")
assert.ErrorContains(t, err, "trigger is nil")
err = sched.ScheduleJob(quartz.NewJobDetail(job, quartz.NewJobKey("job")), expiredTrigger)
assert.Equal(t, err.Error(), "next trigger time is in the past")
assert.ErrorContains(t, err, "next trigger time is in the past")

err = sched.DeleteJob(nil)
assert.Equal(t, err.Error(), "jobKey is nil")
assert.ErrorContains(t, err, "jobKey is nil")

_, err = sched.GetScheduledJob(nil)
assert.Equal(t, err.Error(), "jobKey is nil")
assert.ErrorContains(t, err, "jobKey is nil")
}

func TestSchedulerStartStop(t *testing.T) {
Expand Down
Loading
Loading