1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package msgpipeline2021import (22 "errors"23 "testing"2425 "github.com/foxcpp/maddy/framework/module"26 "github.com/foxcpp/maddy/internal/modify"27 "github.com/foxcpp/maddy/internal/testutils"28)2930func TestMsgPipeline_SenderModifier(t *testing.T) {31 target := testutils.Target{}32 modifier := testutils.Modifier{33 InstName: "test_modifier",34 MailFrom: map[string]string{35 "sender@example.com": "sender2@example.com",36 },37 }38 d := MsgPipeline{39 msgpipelineCfg: msgpipelineCfg{40 globalModifiers: modify.Group{41 Modifiers: []module.Modifier{modifier},42 },43 perSource: map[string]sourceBlock{},44 defaultSource: sourceBlock{45 perRcpt: map[string]*rcptBlock{},46 defaultRcpt: &rcptBlock{47 targets: []module.DeliveryTarget{&target},48 },49 },50 },51 Log: testutils.Logger(t, "msgpipeline"),52 }5354 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})5556 if len(target.Messages) != 1 {57 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))58 }5960 testutils.CheckTestMessage(t, &target, 0, "sender2@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})6162 if modifier.UnclosedStates != 0 {63 t.Fatalf("modifier state objects leak or double-closed, counter: %d", modifier.UnclosedStates)64 }65}6667func TestMsgPipeline_SenderModifier_Multiple(t *testing.T) {68 target := testutils.Target{}69 mod1, mod2 := testutils.Modifier{70 InstName: "first_modifier",71 MailFrom: map[string]string{72 "sender@example.com": "sender2@example.com",73 },74 }, testutils.Modifier{75 InstName: "second_modifier",76 MailFrom: map[string]string{77 "sender2@example.com": "sender3@example.com",78 },79 }80 d := MsgPipeline{81 msgpipelineCfg: msgpipelineCfg{82 globalModifiers: modify.Group{83 Modifiers: []module.Modifier{mod1, mod2},84 },85 perSource: map[string]sourceBlock{},86 defaultSource: sourceBlock{87 perRcpt: map[string]*rcptBlock{},88 defaultRcpt: &rcptBlock{89 targets: []module.DeliveryTarget{&target},90 },91 },92 },93 Log: testutils.Logger(t, "msgpipeline"),94 }9596 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})9798 if len(target.Messages) != 1 {99 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))100 }101102 testutils.CheckTestMessage(t, &target, 0, "sender3@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})103104 if mod1.UnclosedStates != 0 || mod2.UnclosedStates != 0 {105 t.Fatalf("modifier state objects leak or double-closed, counter: %d, %d", mod1.UnclosedStates, mod2.UnclosedStates)106 }107}108109func TestMsgPipeline_SenderModifier_PreDispatch(t *testing.T) {110 target := testutils.Target{InstName: "target"}111 mod := testutils.Modifier{112 InstName: "test_modifier",113 MailFrom: map[string]string{114 "sender@example.com": "sender@example.org",115 },116 }117 d := MsgPipeline{118 msgpipelineCfg: msgpipelineCfg{119 globalModifiers: modify.Group{120 Modifiers: []module.Modifier{mod},121 },122 perSource: map[string]sourceBlock{123 "example.org": {124 perRcpt: map[string]*rcptBlock{},125 defaultRcpt: &rcptBlock{126 targets: []module.DeliveryTarget{&target},127 },128 },129 },130 defaultSource: sourceBlock{rejectErr: errors.New("default src block used")},131 },132 Log: testutils.Logger(t, "msgpipeline"),133 }134135 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})136137 if len(target.Messages) != 1 {138 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))139 }140 testutils.CheckTestMessage(t, &target, 0, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"})141142 if mod.UnclosedStates != 0 {143 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)144 }145}146147func TestMsgPipeline_SenderModifier_PostDispatch(t *testing.T) {148 target := testutils.Target{InstName: "target"}149 mod := testutils.Modifier{150 InstName: "test_modifier",151 MailFrom: map[string]string{152 "sender@example.org": "sender@example.com",153 },154 }155 d := MsgPipeline{156 msgpipelineCfg: msgpipelineCfg{157 perSource: map[string]sourceBlock{158 "example.org": {159 modifiers: modify.Group{160 Modifiers: []module.Modifier{mod},161 },162 perRcpt: map[string]*rcptBlock{},163 defaultRcpt: &rcptBlock{164 targets: []module.DeliveryTarget{&target},165 },166 },167 },168 defaultSource: sourceBlock{rejectErr: errors.New("default src block used")},169 },170 Log: testutils.Logger(t, "msgpipeline"),171 }172173 testutils.DoTestDelivery(t, &d, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"})174175 if len(target.Messages) != 1 {176 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))177 }178 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})179180 if mod.UnclosedStates != 0 {181 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)182 }183}184185func TestMsgPipeline_SenderModifier_PerRcpt(t *testing.T) {186 // Modifier below will be no-op due to implementation limitations.187188 comTarget, orgTarget := testutils.Target{InstName: "com_target"}, testutils.Target{InstName: "org_target"}189 mod := testutils.Modifier{190 InstName: "test_modifier",191 MailFrom: map[string]string{192 "sender@example.com": "sender2@example.com",193 },194 }195 d := MsgPipeline{196 msgpipelineCfg: msgpipelineCfg{197 perSource: map[string]sourceBlock{},198 defaultSource: sourceBlock{199 perRcpt: map[string]*rcptBlock{200 "example.com": {201 modifiers: modify.Group{202 Modifiers: []module.Modifier{mod},203 },204 targets: []module.DeliveryTarget{&comTarget},205 },206 "example.org": {207 modifiers: modify.Group{},208 targets: []module.DeliveryTarget{&orgTarget},209 },210 },211 },212 },213 Log: testutils.Logger(t, "msgpipeline"),214 }215216 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt@example.com", "rcpt@example.org"})217218 if len(comTarget.Messages) != 1 {219 t.Fatalf("wrong amount of messages received for comTarget, want %d, got %d", 1, len(comTarget.Messages))220 }221 testutils.CheckTestMessage(t, &comTarget, 0, "sender@example.com", []string{"rcpt@example.com"})222223 if len(orgTarget.Messages) != 1 {224 t.Fatalf("wrong amount of messages received for orgTarget, want %d, got %d", 1, len(orgTarget.Messages))225 }226 testutils.CheckTestMessage(t, &orgTarget, 0, "sender@example.com", []string{"rcpt@example.org"})227228 if mod.UnclosedStates != 0 {229 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)230 }231}232233func TestMsgPipeline_RcptModifier(t *testing.T) {234 target := testutils.Target{}235 mod := testutils.Modifier{236 InstName: "test_modifier",237 RcptTo: map[string][]string{238 "rcpt1@example.com": []string{"rcpt1-alias@example.com"},239 "rcpt2@example.com": []string{"rcpt2-alias@example.com"},240 },241 }242 d := MsgPipeline{243 msgpipelineCfg: msgpipelineCfg{244 globalModifiers: modify.Group{245 Modifiers: []module.Modifier{mod},246 },247 perSource: map[string]sourceBlock{},248 defaultSource: sourceBlock{249 perRcpt: map[string]*rcptBlock{},250 defaultRcpt: &rcptBlock{251 targets: []module.DeliveryTarget{&target},252 },253 },254 },255 Log: testutils.Logger(t, "msgpipeline"),256 }257258 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})259260 if len(target.Messages) != 1 {261 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))262 }263264 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1-alias@example.com", "rcpt2-alias@example.com"})265266 if mod.UnclosedStates != 0 {267 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)268 }269}270271func TestMsgPipeline_RcptModifier_OriginalRcpt(t *testing.T) {272 target := testutils.Target{}273 mod := testutils.Modifier{274 InstName: "test_modifier",275 RcptTo: map[string][]string{276 "rcpt1@example.com": []string{"rcpt1-alias@example.com"},277 "rcpt2@example.com": []string{"rcpt2-alias@example.com"},278 },279 }280 d := MsgPipeline{281 msgpipelineCfg: msgpipelineCfg{282 globalModifiers: modify.Group{283 Modifiers: []module.Modifier{mod},284 },285 perSource: map[string]sourceBlock{},286 defaultSource: sourceBlock{287 perRcpt: map[string]*rcptBlock{},288 defaultRcpt: &rcptBlock{289 targets: []module.DeliveryTarget{&target},290 },291 },292 },293 Log: testutils.Logger(t, "msgpipeline"),294 }295296 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})297298 if len(target.Messages) != 1 {299 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))300 }301302 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1-alias@example.com", "rcpt2-alias@example.com"})303 original1 := target.Messages[0].MsgMeta.OriginalRcpts["rcpt1-alias@example.com"]304 if original1 != "rcpt1@example.com" {305 t.Errorf("wrong OriginalRcpts value for first rcpt, want %s, got %s", "rcpt1@example.com", original1)306 }307 original2 := target.Messages[0].MsgMeta.OriginalRcpts["rcpt2-alias@example.com"]308 if original2 != "rcpt2@example.com" {309 t.Errorf("wrong OriginalRcpts value for first rcpt, want %s, got %s", "rcpt2@example.com", original2)310 }311312 if mod.UnclosedStates != 0 {313 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)314 }315}316317func TestMsgPipeline_RcptModifier_OriginalRcpt_Multiple(t *testing.T) {318 target := testutils.Target{}319 mod1, mod2 := testutils.Modifier{320 InstName: "first_modifier",321 RcptTo: map[string][]string{322 "rcpt1@example.com": []string{"rcpt1-alias@example.com"},323 "rcpt2@example.com": []string{"rcpt2-alias@example.com"},324 },325 }, testutils.Modifier{326 InstName: "second_modifier",327 RcptTo: map[string][]string{328 "rcpt1-alias@example.com": []string{"rcpt1-alias2@example.com"},329 "rcpt2@example.com": []string{"wtf@example.com"},330 },331 }332 d := MsgPipeline{333 msgpipelineCfg: msgpipelineCfg{334 globalModifiers: modify.Group{335 Modifiers: []module.Modifier{mod1},336 },337 perSource: map[string]sourceBlock{},338 defaultSource: sourceBlock{339 modifiers: modify.Group{340 Modifiers: []module.Modifier{mod2},341 },342 perRcpt: map[string]*rcptBlock{},343 defaultRcpt: &rcptBlock{344 targets: []module.DeliveryTarget{&target},345 },346 },347 },348 Log: testutils.Logger(t, "msgpipeline"),349 }350351 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})352353 if len(target.Messages) != 1 {354 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))355 }356357 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1-alias2@example.com", "rcpt2-alias@example.com"})358 original1 := target.Messages[0].MsgMeta.OriginalRcpts["rcpt1-alias2@example.com"]359 if original1 != "rcpt1@example.com" {360 t.Errorf("wrong OriginalRcpts value for first rcpt, want %s, got %s", "rcpt1@example.com", original1)361 }362 original2 := target.Messages[0].MsgMeta.OriginalRcpts["rcpt2-alias@example.com"]363 if original2 != "rcpt2@example.com" {364 t.Errorf("wrong OriginalRcpts value for first rcpt, want %s, got %s", "rcpt2@example.com", original2)365 }366367 if mod1.UnclosedStates != 0 || mod2.UnclosedStates != 0 {368 t.Fatalf("modifier state objects leak or double-closed, counter: %d, %d", mod1.UnclosedStates, mod2.UnclosedStates)369 }370}371372func TestMsgPipeline_RcptModifier_Multiple(t *testing.T) {373 target := testutils.Target{}374 mod1, mod2 := testutils.Modifier{375 InstName: "first_modifier",376 RcptTo: map[string][]string{377 "rcpt1@example.com": []string{"rcpt1-alias@example.com"},378 "rcpt2@example.com": []string{"rcpt2-alias@example.com"},379 },380 }, testutils.Modifier{381 InstName: "second_modifier",382 RcptTo: map[string][]string{383 "rcpt1-alias@example.com": []string{"rcpt1-alias2@example.com"},384 "rcpt2@example.com": []string{"wtf@example.com"},385 },386 }387 d := MsgPipeline{388 msgpipelineCfg: msgpipelineCfg{389 globalModifiers: modify.Group{390 Modifiers: []module.Modifier{mod1, mod2},391 },392 perSource: map[string]sourceBlock{},393 defaultSource: sourceBlock{394 perRcpt: map[string]*rcptBlock{},395 defaultRcpt: &rcptBlock{396 targets: []module.DeliveryTarget{&target},397 },398 },399 },400 Log: testutils.Logger(t, "msgpipeline"),401 }402403 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})404405 if len(target.Messages) != 1 {406 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))407 }408409 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1-alias2@example.com", "rcpt2-alias@example.com"})410411 if mod1.UnclosedStates != 0 || mod2.UnclosedStates != 0 {412 t.Fatalf("modifier state objects leak or double-closed, counter: %d, %d", mod1.UnclosedStates, mod2.UnclosedStates)413 }414}415416func TestMsgPipeline_RcptModifier_PreDispatch(t *testing.T) {417 target := testutils.Target{}418 mod1, mod2 := testutils.Modifier{419 InstName: "first_modifier",420 RcptTo: map[string][]string{421 "rcpt1@example.com": []string{"rcpt1-alias@example.com"},422 "rcpt2@example.com": []string{"rcpt2-alias@example.com"},423 },424 }, testutils.Modifier{425 InstName: "second_modifier",426 RcptTo: map[string][]string{427 "rcpt1-alias@example.com": []string{"rcpt1-alias2@example.com"},428 "rcpt2@example.com": []string{"wtf@example.com"},429 },430 }431 d := MsgPipeline{432 msgpipelineCfg: msgpipelineCfg{433 globalModifiers: modify.Group{434 Modifiers: []module.Modifier{mod1},435 },436 perSource: map[string]sourceBlock{},437 defaultSource: sourceBlock{438 modifiers: modify.Group{Modifiers: []module.Modifier{mod2}},439 perRcpt: map[string]*rcptBlock{440 "rcpt2-alias@example.com": {441 targets: []module.DeliveryTarget{&target},442 },443 "rcpt1-alias2@example.com": {444 targets: []module.DeliveryTarget{&target},445 },446 },447 defaultRcpt: &rcptBlock{448 rejectErr: errors.New("default rcpt is used"),449 },450 },451 },452 Log: testutils.Logger(t, "msgpipeline"),453 }454455 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})456457 if len(target.Messages) != 1 {458 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))459 }460461 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1-alias2@example.com", "rcpt2-alias@example.com"})462463 if mod1.UnclosedStates != 0 || mod2.UnclosedStates != 0 {464 t.Fatalf("modifier state objects leak or double-closed, counter: %d, %d", mod1.UnclosedStates, mod2.UnclosedStates)465 }466}467468func TestMsgPipeline_RcptModifier_PostDispatch(t *testing.T) {469 target := testutils.Target{}470 mod := testutils.Modifier{471 InstName: "test_modifier",472 RcptTo: map[string][]string{473 "rcpt1@example.com": []string{"rcpt1@example.org"},474 "rcpt2@example.com": []string{"rcpt2@example.org"},475 },476 }477 d := MsgPipeline{478 msgpipelineCfg: msgpipelineCfg{479 perSource: map[string]sourceBlock{},480 defaultSource: sourceBlock{481 perRcpt: map[string]*rcptBlock{482 "example.com": {483 modifiers: modify.Group{484 Modifiers: []module.Modifier{mod},485 },486 targets: []module.DeliveryTarget{&target},487 },488 "example.org": {489 rejectErr: errors.New("wrong rcpt block is used"),490 },491 },492 defaultRcpt: &rcptBlock{493 rejectErr: errors.New("default rcpt is used"),494 },495 },496 },497 Log: testutils.Logger(t, "msgpipeline"),498 }499500 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})501502 if len(target.Messages) != 1 {503 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))504 }505506 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1@example.org", "rcpt2@example.org"})507508 if mod.UnclosedStates != 0 {509 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)510 }511}512513func TestMsgPipeline_GlobalModifier_Errors(t *testing.T) {514 target := testutils.Target{}515 mod := testutils.Modifier{516 InstName: "test_modifier",517 InitErr: errors.New("1"),518 MailFromErr: errors.New("2"),519 RcptToErr: errors.New("3"),520 BodyErr: errors.New("4"),521 }522 d := MsgPipeline{523 msgpipelineCfg: msgpipelineCfg{524 globalModifiers: modify.Group{Modifiers: []module.Modifier{&mod}},525 perSource: map[string]sourceBlock{},526 defaultSource: sourceBlock{527 defaultRcpt: &rcptBlock{528 targets: []module.DeliveryTarget{&target},529 },530 },531 },532 Log: testutils.Logger(t, "msgpipeline"),533 }534535 t.Run("init err", func(t *testing.T) {536 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})537 if err == nil {538 t.Fatal("expected error")539 }540 })541542 mod.InitErr = nil543544 t.Run("mail from err", func(t *testing.T) {545 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})546 if err == nil {547 t.Fatal("expected error")548 }549 })550551 mod.MailFromErr = nil552553 t.Run("rcpt to err", func(t *testing.T) {554 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})555 if err == nil {556 t.Fatal("expected error")557 }558 })559560 mod.RcptToErr = nil561562 t.Run("body err", func(t *testing.T) {563 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})564 if err == nil {565 t.Fatal("expected error")566 }567 })568569 mod.BodyErr = nil570571 t.Run("no err", func(t *testing.T) {572 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})573 })574575 if mod.UnclosedStates != 0 {576 t.Fatalf("modifier state objects leak or double-closed, counter: %d", mod.UnclosedStates)577 }578}579580func TestMsgPipeline_SourceModifier_Errors(t *testing.T) {581 target := testutils.Target{}582 mod := testutils.Modifier{583 InstName: "test_modifier",584 InitErr: errors.New("1"),585 MailFromErr: errors.New("2"),586 RcptToErr: errors.New("3"),587 BodyErr: errors.New("4"),588 }589 // Added to make sure it is freed properly too.590 globalMod := testutils.Modifier{}591 d := MsgPipeline{592 msgpipelineCfg: msgpipelineCfg{593 perSource: map[string]sourceBlock{},594 globalModifiers: modify.Group{Modifiers: []module.Modifier{&globalMod}},595 defaultSource: sourceBlock{596 modifiers: modify.Group{Modifiers: []module.Modifier{&mod}},597 defaultRcpt: &rcptBlock{598 targets: []module.DeliveryTarget{&target},599 },600 },601 },602 Log: testutils.Logger(t, "msgpipeline"),603 }604605 t.Run("init err", func(t *testing.T) {606 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})607 if err == nil {608 t.Fatal("expected error")609 }610 })611612 mod.InitErr = nil613614 t.Run("mail from err", func(t *testing.T) {615 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})616 if err == nil {617 t.Fatal("expected error")618 }619 })620621 mod.MailFromErr = nil622623 t.Run("rcpt to err", func(t *testing.T) {624 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})625 if err == nil {626 t.Fatal("expected error")627 }628 })629630 mod.RcptToErr = nil631632 t.Run("body err", func(t *testing.T) {633 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})634 if err == nil {635 t.Fatal("expected error")636 }637 })638639 mod.BodyErr = nil640641 t.Run("no err", func(t *testing.T) {642 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})643 })644645 if mod.UnclosedStates != 0 || globalMod.UnclosedStates != 0 {646 t.Fatalf("modifier state objects leak or double-closed, counters: %d, %d",647 mod.UnclosedStates, globalMod.UnclosedStates)648 }649}650651func TestMsgPipeline_RcptModifier_Errors(t *testing.T) {652 target := testutils.Target{}653 mod := testutils.Modifier{654 InstName: "test_modifier",655 InitErr: errors.New("1"),656 RcptToErr: errors.New("3"),657 }658 // Added to make sure it is freed properly too.659 globalMod := testutils.Modifier{}660 sourceMod := testutils.Modifier{}661662 d := MsgPipeline{663 msgpipelineCfg: msgpipelineCfg{664 perSource: map[string]sourceBlock{},665 globalModifiers: modify.Group{Modifiers: []module.Modifier{&globalMod}},666 defaultSource: sourceBlock{667 modifiers: modify.Group{Modifiers: []module.Modifier{&sourceMod}},668 defaultRcpt: &rcptBlock{669 modifiers: modify.Group{Modifiers: []module.Modifier{&mod}},670 targets: []module.DeliveryTarget{&target},671 },672 },673 },674 Log: testutils.Logger(t, "msgpipeline"),675 }676677 t.Run("init err", func(t *testing.T) {678 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})679 if err == nil {680 t.Fatal("expected error")681 }682 })683684 mod.InitErr = nil685686 // MailFromErr test is inapplicable since RewriteSender is not called for per-rcpt687 // modifiers.688689 t.Run("rcpt to err", func(t *testing.T) {690 _, err := testutils.DoTestDeliveryErr(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})691 if err == nil {692 t.Fatal("expected error")693 }694 })695696 mod.RcptToErr = nil697698 // BodyErr test is inapplicable since RewriteBody is not called for per-rcpt699 // modifiers.700701 t.Run("no err", func(t *testing.T) {702 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})703 })704705 if mod.UnclosedStates != 0 || globalMod.UnclosedStates != 0 || sourceMod.UnclosedStates != 0 {706 t.Fatalf("modifier state objects leak or double-closed, counters: %d, %d, %d",707 mod.UnclosedStates, globalMod.UnclosedStates, sourceMod.UnclosedStates)708 }709}