package parsers import ( "context" "errors" "fmt" . "github.com/0xERR0R/blocky/helpertest" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("ForEach", func() { var lines SeriesParser[string] BeforeEach(func() { lines = Lines(linesReader( "first", "second", "third", )) }) It("should iterate and hide io.EOF", func() { list := iteratorToList(func(cb func(string) error) error { return ForEach(context.Background(), lines, cb) }) Expect(list).Should(Equal([]string{"first", "second", "third"})) }) It("should return callback errors", func() { expectedErr := errors.New("fail") err := ForEach(context.Background(), lines, func(line string) error { return expectedErr }) Expect(err).ShouldNot(Succeed()) Expect(err).Should(MatchError(expectedErr)) Expect(err.Error()).Should(HavePrefix("line 1: ")) }) It("should return parser errors", func() { lines := Hosts(linesReader( "invalid line", )) err := ForEach(context.Background(), lines, func(*HostsIterator) error { Fail("callback should not be called") return nil }) Expect(err).ShouldNot(Succeed()) Expect(err.Error()).Should(HavePrefix("line 1: ")) }) It("should stop when context is done", func() { ctx, cancel := context.WithCancel(context.Background()) err := ForEach(ctx, lines, func(line string) error { if ctx.Err() != nil { Fail("callback should not be called") } cancel() return nil }) Expect(err).ShouldNot(Succeed()) Expect(err).Should(MatchError(context.Canceled)) Expect(err.Error()).Should(HavePrefix("line 1: ")) }) It("should not start if context is already done", func() { ctx, cancel := context.WithCancel(context.Background()) cancel() err := ForEach(ctx, lines, func(line string) error { Fail("callback should not be called") return nil }) Expect(err).ShouldNot(Succeed()) Expect(err).Should(MatchError(context.Canceled)) Expect(err.Error()).Should(HavePrefix("line 0: ")) }) }) var _ = Describe("ErrWithPosition", func() { When("err is nil", func() { It("returns nil", func() { inner := errors.New("inner") lines := Lines(linesReader( "first", "second", )) _, err := lines.Next(context.Background()) Expect(err).Should(Succeed()) err = ErrWithPosition(lines, inner) Expect(err).ShouldNot(Succeed()) Expect(err.Error()).Should(Equal("line 1: inner")) _, err = lines.Next(context.Background()) Expect(err).Should(Succeed()) err = ErrWithPosition(lines, inner) Expect(err).ShouldNot(Succeed()) Expect(err.Error()).Should(Equal("line 2: inner")) }) }) When("err is nil", func() { It("returns nil", func() { err := ErrWithPosition[any](nil, nil) Expect(err).Should(Succeed()) }) }) }) var _ = Describe("NonResumableError", func() { Describe("IsNonResumableErr", func() { It("should return the inner error", func() { inner := errors.New("inner") Expect(IsNonResumableErr(inner)).Should(BeFalse()) err := NewNonResumableError(inner) Expect(IsNonResumableErr(err)).Should(BeTrue()) }) }) Describe("Error", func() { It("should return error message", func() { inner := errors.New("inner") err := NewNonResumableError(inner) Expect(err.Error()).Should(Equal("non resumable parse error: inner")) }) }) Describe("Unwrap", func() { It("should return the inner error", func() { inner := errors.New("inner") err := NewNonResumableError(inner) Expect(errors.Unwrap(err)).Should(Equal(inner)) Expect(errors.Is(err, inner)).Should(BeTrue()) }) }) }) func iteratorToList[T any](forEach func(func(T) error) error) []T { var res []T err := forEach(func(t T) error { res = append(res, t) return nil }) Expect(err).Should(Succeed()) return res } type mockParser[T any] struct{ MockCallSequence[T] } func newMockParser[T any](driver func(chan<- T, chan<- error)) SeriesParser[T] { return &mockParser[T]{NewMockCallSequence(driver)} } func (m *mockParser[T]) Next(ctx context.Context) (_ T, rerr error) { defer func() { if rerr != nil && IsNonResumableErr(rerr) { m.Close() } }() if err := ctx.Err(); err != nil { var zero T return zero, NewNonResumableError(err) } return m.Call() } func (m *mockParser[T]) Position() string { return fmt.Sprintf("call %d", m.CallCount()) }