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 "context"23 "errors"24 "testing"2526 "github.com/emersion/go-message/textproto"27 "github.com/emersion/go-smtp"28 "github.com/foxcpp/maddy/framework/buffer"29 "github.com/foxcpp/maddy/framework/module"30 "github.com/foxcpp/maddy/internal/modify"31 "github.com/foxcpp/maddy/internal/testutils"32)3334func TestMsgPipeline_AllToTarget(t *testing.T) {35 target := testutils.Target{}36 d := MsgPipeline{37 msgpipelineCfg: msgpipelineCfg{38 perSource: map[string]sourceBlock{},39 defaultSource: sourceBlock{40 perRcpt: map[string]*rcptBlock{},41 defaultRcpt: &rcptBlock{42 targets: []module.DeliveryTarget{&target},43 },44 },45 },46 Log: testutils.Logger(t, "msgpipeline"),47 }4849 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})5051 if len(target.Messages) != 1 {52 t.Fatalf("wrong amount of messages received, want %d, got %d", 1, len(target.Messages))53 }5455 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})56}5758func TestMsgPipeline_PerSourceDomainSplit(t *testing.T) {59 orgTarget, comTarget := testutils.Target{InstName: "orgTarget"}, testutils.Target{InstName: "comTarget"}60 d := MsgPipeline{61 msgpipelineCfg: msgpipelineCfg{62 perSource: map[string]sourceBlock{63 "example.com": {64 perRcpt: map[string]*rcptBlock{},65 defaultRcpt: &rcptBlock{66 targets: []module.DeliveryTarget{&comTarget},67 },68 },69 "example.org": {70 perRcpt: map[string]*rcptBlock{},71 defaultRcpt: &rcptBlock{72 targets: []module.DeliveryTarget{&orgTarget},73 },74 },75 },76 defaultSource: sourceBlock{rejectErr: errors.New("default src block used")},77 },78 Log: testutils.Logger(t, "msgpipeline"),79 }8081 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})82 testutils.DoTestDelivery(t, &d, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"})8384 if len(comTarget.Messages) != 1 {85 t.Fatalf("wrong amount of messages received for comTarget, want %d, got %d", 1, len(comTarget.Messages))86 }87 testutils.CheckTestMessage(t, &comTarget, 0, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.com"})8889 if len(orgTarget.Messages) != 1 {90 t.Fatalf("wrong amount of messages received for orgTarget, want %d, got %d", 1, len(orgTarget.Messages))91 }92 testutils.CheckTestMessage(t, &orgTarget, 0, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"})93}9495func TestMsgPipeline_SourceIn(t *testing.T) {96 tblTarget, comTarget := testutils.Target{InstName: "tblTarget"}, testutils.Target{InstName: "comTarget"}97 d := MsgPipeline{98 msgpipelineCfg: msgpipelineCfg{99 sourceIn: []sourceIn{100 {101 t: testutils.Table{},102 block: sourceBlock{rejectErr: errors.New("non-matching block was used")},103 },104 {105 t: testutils.Table{Err: errors.New("this one will fail")},106 block: sourceBlock{rejectErr: errors.New("failing block was used")},107 },108 {109 t: testutils.Table{110 M: map[string]string{111 "specific@example.com": "",112 },113 },114 block: sourceBlock{115 perRcpt: map[string]*rcptBlock{},116 defaultRcpt: &rcptBlock{117 targets: []module.DeliveryTarget{&tblTarget},118 },119 },120 },121 },122 perSource: map[string]sourceBlock{123 "example.com": {124 perRcpt: map[string]*rcptBlock{},125 defaultRcpt: &rcptBlock{126 targets: []module.DeliveryTarget{&comTarget},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{"rcpt@example.com"})136 testutils.DoTestDelivery(t, &d, "specific@example.com", []string{"rcpt@example.com"})137138 if len(comTarget.Messages) != 1 {139 t.Fatalf("wrong amount of messages received for comTarget, want %d, got %d", 1, len(comTarget.Messages))140 }141 testutils.CheckTestMessage(t, &comTarget, 0, "sender@example.com", []string{"rcpt@example.com"})142143 if len(tblTarget.Messages) != 1 {144 t.Fatalf("wrong amount of messages received for orgTarget, want %d, got %d", 1, len(tblTarget.Messages))145 }146 testutils.CheckTestMessage(t, &tblTarget, 0, "specific@example.com", []string{"rcpt@example.com"})147}148149func TestMsgPipeline_EmptyMAILFROM(t *testing.T) {150 target := testutils.Target{InstName: "target"}151 d := MsgPipeline{152 msgpipelineCfg: msgpipelineCfg{153 perSource: map[string]sourceBlock{},154 defaultSource: sourceBlock{155 perRcpt: map[string]*rcptBlock{},156 defaultRcpt: &rcptBlock{157 targets: []module.DeliveryTarget{&target},158 },159 },160 },161 Log: testutils.Logger(t, "msgpipeline"),162 }163164 testutils.DoTestDelivery(t, &d, "", []string{"rcpt1@example.com", "rcpt2@example.com"})165166 if len(target.Messages) != 1 {167 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))168 }169 testutils.CheckTestMessage(t, &target, 0, "", []string{"rcpt1@example.com", "rcpt2@example.com"})170}171172func TestMsgPipeline_EmptyMAILFROM_ExplicitDest(t *testing.T) {173 target := testutils.Target{InstName: "target"}174 d := MsgPipeline{175 msgpipelineCfg: msgpipelineCfg{176 perSource: map[string]sourceBlock{177 "": {178 perRcpt: map[string]*rcptBlock{},179 defaultRcpt: &rcptBlock{180 targets: []module.DeliveryTarget{&target},181 },182 },183 },184 defaultSource: sourceBlock{rejectErr: errors.New("default src block used")},185 },186 Log: testutils.Logger(t, "msgpipeline"),187 }188189 testutils.DoTestDelivery(t, &d, "", []string{"rcpt1@example.com", "rcpt2@example.com"})190191 if len(target.Messages) != 1 {192 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))193 }194 testutils.CheckTestMessage(t, &target, 0, "", []string{"rcpt1@example.com", "rcpt2@example.com"})195}196197func TestMsgPipeline_PerRcptAddrSplit(t *testing.T) {198 target1, target2 := testutils.Target{InstName: "target1"}, testutils.Target{InstName: "target2"}199 d := MsgPipeline{200 msgpipelineCfg: msgpipelineCfg{201 perSource: map[string]sourceBlock{},202 defaultSource: sourceBlock{203 perRcpt: map[string]*rcptBlock{204 "rcpt1@example.com": {205 targets: []module.DeliveryTarget{&target1},206 },207 "rcpt2@example.com": {208 targets: []module.DeliveryTarget{&target2},209 },210 },211 defaultRcpt: &rcptBlock{212 rejectErr: errors.New("defaultRcpt block used"),213 },214 },215 },216 Log: testutils.Logger(t, "msgpipeline"),217 }218219 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com"})220 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt2@example.com"})221222 if len(target1.Messages) != 1 {223 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 1, len(target1.Messages))224 }225 testutils.CheckTestMessage(t, &target1, 0, "sender@example.com", []string{"rcpt1@example.com"})226227 if len(target2.Messages) != 1 {228 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 1, len(target2.Messages))229 }230 testutils.CheckTestMessage(t, &target2, 0, "sender@example.com", []string{"rcpt2@example.com"})231}232233func TestMsgPipeline_PerRcptDomainSplit(t *testing.T) {234 target1, target2 := testutils.Target{InstName: "target1"}, testutils.Target{InstName: "target2"}235 d := MsgPipeline{236 msgpipelineCfg: msgpipelineCfg{237 perSource: map[string]sourceBlock{},238 defaultSource: sourceBlock{239 perRcpt: map[string]*rcptBlock{240 "example.com": {241 targets: []module.DeliveryTarget{&target1},242 },243 "example.org": {244 targets: []module.DeliveryTarget{&target2},245 },246 },247 defaultRcpt: &rcptBlock{248 rejectErr: errors.New("defaultRcpt block used"),249 },250 },251 },252 Log: testutils.Logger(t, "msgpipeline"),253 }254255 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "rcpt2@example.org"})256 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.org", "rcpt2@example.com"})257258 if len(target1.Messages) != 2 {259 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 2, len(target1.Messages))260 }261 testutils.CheckTestMessage(t, &target1, 0, "sender@example.com", []string{"rcpt1@example.com"})262 testutils.CheckTestMessage(t, &target1, 1, "sender@example.com", []string{"rcpt2@example.com"})263264 if len(target2.Messages) != 2 {265 t.Errorf("wrong amount of messages received for target2, want %d, got %d", 2, len(target2.Messages))266 }267 testutils.CheckTestMessage(t, &target2, 0, "sender@example.com", []string{"rcpt2@example.org"})268 testutils.CheckTestMessage(t, &target2, 1, "sender@example.com", []string{"rcpt1@example.org"})269}270271func TestMsgPipeline_DestInSplit(t *testing.T) {272 target1, target2 := testutils.Target{InstName: "target1"}, testutils.Target{InstName: "target2"}273 d := MsgPipeline{274 msgpipelineCfg: msgpipelineCfg{275 perSource: map[string]sourceBlock{},276 defaultSource: sourceBlock{277 rcptIn: []rcptIn{278 {279 t: testutils.Table{},280 block: &rcptBlock{rejectErr: errors.New("non-matching block was used")},281 },282 {283 t: testutils.Table{Err: errors.New("nope")},284 block: &rcptBlock{rejectErr: errors.New("failing block was used")},285 },286 {287 t: testutils.Table{288 M: map[string]string{289 "specific@example.com": "",290 },291 },292 block: &rcptBlock{293 targets: []module.DeliveryTarget{&target2},294 },295 },296 },297 perRcpt: map[string]*rcptBlock{298 "example.com": {299 targets: []module.DeliveryTarget{&target1},300 },301 },302 defaultRcpt: &rcptBlock{303 rejectErr: errors.New("defaultRcpt block used"),304 },305 },306 },307 Log: testutils.Logger(t, "msgpipeline"),308 }309310 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt1@example.com", "specific@example.com"})311312 if len(target1.Messages) != 1 {313 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 1, len(target1.Messages))314 }315 testutils.CheckTestMessage(t, &target1, 0, "sender@example.com", []string{"rcpt1@example.com"})316317 if len(target2.Messages) != 1 {318 t.Errorf("wrong amount of messages received for target2, want %d, got %d", 1, len(target2.Messages))319 }320 testutils.CheckTestMessage(t, &target2, 0, "sender@example.com", []string{"specific@example.com"})321}322323func TestMsgPipeline_PerSourceAddrAndDomainSplit(t *testing.T) {324 target1, target2 := testutils.Target{InstName: "target1"}, testutils.Target{InstName: "target2"}325 d := MsgPipeline{326 msgpipelineCfg: msgpipelineCfg{327 perSource: map[string]sourceBlock{328 "sender1@example.com": {329 perRcpt: map[string]*rcptBlock{},330 defaultRcpt: &rcptBlock{331 targets: []module.DeliveryTarget{&target1},332 },333 },334 "example.com": {335 perRcpt: map[string]*rcptBlock{},336 defaultRcpt: &rcptBlock{337 targets: []module.DeliveryTarget{&target2},338 },339 },340 },341 defaultSource: sourceBlock{rejectErr: errors.New("default src block used")},342 },343 Log: testutils.Logger(t, "msgpipeline"),344 }345346 testutils.DoTestDelivery(t, &d, "sender1@example.com", []string{"rcpt@example.com"})347 testutils.DoTestDelivery(t, &d, "sender2@example.com", []string{"rcpt@example.com"})348349 if len(target1.Messages) != 1 {350 t.Fatalf("wrong amount of messages received for target1, want %d, got %d", 1, len(target1.Messages))351 }352 testutils.CheckTestMessage(t, &target1, 0, "sender1@example.com", []string{"rcpt@example.com"})353354 if len(target2.Messages) != 1 {355 t.Fatalf("wrong amount of messages received for target2, want %d, got %d", 1, len(target2.Messages))356 }357 testutils.CheckTestMessage(t, &target2, 0, "sender2@example.com", []string{"rcpt@example.com"})358}359360func TestMsgPipeline_PerSourceReject(t *testing.T) {361 target := testutils.Target{}362 d := MsgPipeline{363 msgpipelineCfg: msgpipelineCfg{364 perSource: map[string]sourceBlock{365 "sender1@example.com": {366 perRcpt: map[string]*rcptBlock{},367 defaultRcpt: &rcptBlock{368 targets: []module.DeliveryTarget{&target},369 },370 },371 "example.com": {372 perRcpt: map[string]*rcptBlock{},373 rejectErr: errors.New("go away"),374 },375 },376 defaultSource: sourceBlock{rejectErr: errors.New("go away")},377 },378 Log: testutils.Logger(t, "msgpipeline"),379 }380381 testutils.DoTestDelivery(t, &d, "sender1@example.com", []string{"rcpt@example.com"})382383 _, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.com")384 if err == nil {385 t.Error("expected error for delivery.Start, got nil")386 }387388 _, err = d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.org")389 if err == nil {390 t.Error("expected error for delivery.Start, got nil")391 }392}393394func TestMsgPipeline_PerRcptReject(t *testing.T) {395 target := testutils.Target{}396 d := MsgPipeline{397 msgpipelineCfg: msgpipelineCfg{398 perSource: map[string]sourceBlock{},399 defaultSource: sourceBlock{400 perRcpt: map[string]*rcptBlock{401 "rcpt1@example.com": {402 targets: []module.DeliveryTarget{&target},403 },404 "example.com": {405 rejectErr: errors.New("go away"),406 },407 },408 defaultRcpt: &rcptBlock{409 rejectErr: errors.New("go away"),410 },411 },412 },413 Log: testutils.Logger(t, "msgpipeline"),414 }415416 delivery, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender@example.com")417 if err != nil {418 t.Fatalf("unexpected Start err: %v", err)419 }420 defer func() {421 if err := delivery.Abort(context.Background()); err != nil {422 t.Fatalf("unexpected Abort err: %v", err)423 }424 }()425426 if err := delivery.AddRcpt(context.Background(), "rcpt2@example.com", smtp.RcptOptions{}); err == nil {427 t.Fatalf("expected error for delivery.AddRcpt(rcpt2@example.com), got nil")428 }429 if err := delivery.AddRcpt(context.Background(), "rcpt1@example.com", smtp.RcptOptions{}); err != nil {430 t.Fatalf("unexpected AddRcpt err for %s: %v", "rcpt1@example.com", err)431 }432 if err := delivery.Body(context.Background(), textproto.Header{}, buffer.MemoryBuffer{Slice: []byte("foobar")}); err != nil {433 t.Fatalf("unexpected Body err: %v", err)434 }435 if err := delivery.Commit(context.Background()); err != nil {436 t.Fatalf("unexpected Commit err: %v", err)437 }438}439440func TestMsgPipeline_PostmasterRcpt(t *testing.T) {441 target := testutils.Target{}442 d := MsgPipeline{443 msgpipelineCfg: msgpipelineCfg{444 perSource: map[string]sourceBlock{},445 defaultSource: sourceBlock{446 perRcpt: map[string]*rcptBlock{447 "postmaster": {448 targets: []module.DeliveryTarget{&target},449 },450 "example.com": {451 rejectErr: errors.New("go away"),452 },453 },454 defaultRcpt: &rcptBlock{455 rejectErr: errors.New("go away"),456 },457 },458 },459 Log: testutils.Logger(t, "msgpipeline"),460 }461462 testutils.DoTestDelivery(t, &d, "disappointed-user@example.com", []string{"postmaster"})463 if len(target.Messages) != 1 {464 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))465 }466 testutils.CheckTestMessage(t, &target, 0, "disappointed-user@example.com", []string{"postmaster"})467}468469func TestMsgPipeline_PostmasterSrc(t *testing.T) {470 target := testutils.Target{}471 d := MsgPipeline{472 msgpipelineCfg: msgpipelineCfg{473 perSource: map[string]sourceBlock{474 "postmaster": {475 perRcpt: map[string]*rcptBlock{},476 defaultRcpt: &rcptBlock{477 targets: []module.DeliveryTarget{&target},478 },479 },480 "example.com": {481 rejectErr: errors.New("go away"),482 },483 },484 defaultSource: sourceBlock{485 rejectErr: errors.New("go away"),486 },487 },488 Log: testutils.Logger(t, "msgpipeline"),489 }490491 testutils.DoTestDelivery(t, &d, "postmaster", []string{"disappointed-user@example.com"})492 if len(target.Messages) != 1 {493 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))494 }495 testutils.CheckTestMessage(t, &target, 0, "postmaster", []string{"disappointed-user@example.com"})496}497498func TestMsgPipeline_CaseInsensetiveMatch_Src(t *testing.T) {499 target := testutils.Target{}500 d := MsgPipeline{501 msgpipelineCfg: msgpipelineCfg{502 perSource: map[string]sourceBlock{503 "postmaster": {504 perRcpt: map[string]*rcptBlock{},505 defaultRcpt: &rcptBlock{506 targets: []module.DeliveryTarget{&target},507 },508 },509 "sender@example.com": {510 perRcpt: map[string]*rcptBlock{},511 defaultRcpt: &rcptBlock{512 targets: []module.DeliveryTarget{&target},513 },514 },515 "example.com": {516 perRcpt: map[string]*rcptBlock{},517 defaultRcpt: &rcptBlock{518 targets: []module.DeliveryTarget{&target},519 },520 },521 },522 defaultSource: sourceBlock{523 rejectErr: errors.New("go away"),524 },525 },526 Log: testutils.Logger(t, "msgpipeline"),527 }528529 testutils.DoTestDelivery(t, &d, "POSTMastER", []string{"disappointed-user@example.com"})530 testutils.DoTestDelivery(t, &d, "SenDeR@EXAMPLE.com", []string{"disappointed-user@example.com"})531 testutils.DoTestDelivery(t, &d, "sender@exAMPle.com", []string{"disappointed-user@example.com"})532 if len(target.Messages) != 3 {533 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 3, len(target.Messages))534 }535 testutils.CheckTestMessage(t, &target, 0, "POSTMastER", []string{"disappointed-user@example.com"})536 testutils.CheckTestMessage(t, &target, 1, "SenDeR@EXAMPLE.com", []string{"disappointed-user@example.com"})537 testutils.CheckTestMessage(t, &target, 2, "sender@exAMPle.com", []string{"disappointed-user@example.com"})538}539540func TestMsgPipeline_CaseInsensetiveMatch_Rcpt(t *testing.T) {541 target := testutils.Target{}542 d := MsgPipeline{543 msgpipelineCfg: msgpipelineCfg{544 perSource: map[string]sourceBlock{},545 defaultSource: sourceBlock{546 perRcpt: map[string]*rcptBlock{547 "postmaster": {548 targets: []module.DeliveryTarget{&target},549 },550 "sender@example.com": {551 targets: []module.DeliveryTarget{&target},552 },553 "example.com": {554 targets: []module.DeliveryTarget{&target},555 },556 },557 defaultRcpt: &rcptBlock{558 rejectErr: errors.New("wtf"),559 },560 },561 },562 Log: testutils.Logger(t, "msgpipeline"),563 }564565 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"POSTMastER"})566 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"SenDeR@EXAMPLE.com"})567 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"sender@exAMPle.com"})568 if len(target.Messages) != 3 {569 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 3, len(target.Messages))570 }571 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"POSTMastER"})572 testutils.CheckTestMessage(t, &target, 1, "sender@example.com", []string{"SenDeR@EXAMPLE.com"})573 testutils.CheckTestMessage(t, &target, 2, "sender@example.com", []string{"sender@exAMPle.com"})574}575576func TestMsgPipeline_UnicodeNFC_Rcpt(t *testing.T) {577 target := testutils.Target{}578 d := MsgPipeline{579 msgpipelineCfg: msgpipelineCfg{580 perSource: map[string]sourceBlock{},581 defaultSource: sourceBlock{582 perRcpt: map[string]*rcptBlock{583 "rcpt@é.example.com": {584 targets: []module.DeliveryTarget{&target},585 },586 "é.example.com": {587 targets: []module.DeliveryTarget{&target},588 },589 },590 defaultRcpt: &rcptBlock{591 rejectErr: errors.New("wtf"),592 },593 },594 },595 Log: testutils.Logger(t, "msgpipeline"),596 }597598 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"rcpt@E\u0301.EXAMPLE.com"})599 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"f@E\u0301.exAMPle.com"})600 if len(target.Messages) != 2 {601 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 2, len(target.Messages))602 }603 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"rcpt@E\u0301.EXAMPLE.com"})604 testutils.CheckTestMessage(t, &target, 1, "sender@example.com", []string{"f@E\u0301.exAMPle.com"})605}606607func TestMsgPipeline_MalformedSource(t *testing.T) {608 target := testutils.Target{}609 d := MsgPipeline{610 msgpipelineCfg: msgpipelineCfg{611 perSource: map[string]sourceBlock{},612 defaultSource: sourceBlock{613 perRcpt: map[string]*rcptBlock{614 "postmaster": {615 targets: []module.DeliveryTarget{&target},616 },617 "sender@example.com": {618 targets: []module.DeliveryTarget{&target},619 },620 "example.com": {621 targets: []module.DeliveryTarget{&target},622 },623 },624 },625 },626 Log: testutils.Logger(t, "msgpipeline"),627 }628629 // Simple checks for violations that can make msgpipeline misbehave.630 for _, addr := range []string{"not_postmaster_but_no_at_sign", "@no_mailbox", "no_domain@"} {631 _, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, addr)632 if err == nil {633 t.Errorf("%s is accepted as valid address", addr)634 }635 }636}637638func TestMsgPipeline_TwoRcptToOneTarget(t *testing.T) {639 target := testutils.Target{}640 d := MsgPipeline{641 msgpipelineCfg: msgpipelineCfg{642 perSource: map[string]sourceBlock{},643 defaultSource: sourceBlock{644 perRcpt: map[string]*rcptBlock{645 "example.com": {646 targets: []module.DeliveryTarget{&target},647 },648 "example.org": {649 targets: []module.DeliveryTarget{&target},650 },651 },652 },653 },654 Log: testutils.Logger(t, "msgpipeline"),655 }656657 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"recipient@example.com", "recipient@example.org"})658659 if len(target.Messages) != 1 {660 t.Fatalf("wrong amount of messages received for target, want %d, got %d", 1, len(target.Messages))661 }662 testutils.CheckTestMessage(t, &target, 0, "sender@example.com", []string{"recipient@example.com", "recipient@example.org"})663}664665func TestMsgPipeline_multi_alias(t *testing.T) {666 target1, target2 := testutils.Target{InstName: "target1"}, testutils.Target{InstName: "target2"}667 mod := testutils.Modifier{668 RcptTo: map[string][]string{669 "recipient@example.com": []string{670 "recipient-1@example.org",671 "recipient-2@example.net",672 },673 },674 }675 d := MsgPipeline{676 msgpipelineCfg: msgpipelineCfg{677 perSource: map[string]sourceBlock{},678 defaultSource: sourceBlock{679 modifiers: modify.Group{680 Modifiers: []module.Modifier{mod},681 },682 perRcpt: map[string]*rcptBlock{683 "example.org": {684 targets: []module.DeliveryTarget{&target1},685 },686 "example.net": {687 targets: []module.DeliveryTarget{&target2},688 },689 },690 },691 },692 Log: testutils.Logger(t, "msgpipeline"),693 }694695 testutils.DoTestDelivery(t, &d, "sender@example.com", []string{"recipient@example.com"})696697 if len(target1.Messages) != 1 {698 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 1, len(target1.Messages))699 }700 testutils.CheckTestMessage(t, &target1, 0, "sender@example.com", []string{"recipient-1@example.org"})701702 if len(target2.Messages) != 1 {703 t.Errorf("wrong amount of messages received for target1, want %d, got %d", 1, len(target2.Messages))704 }705 testutils.CheckTestMessage(t, &target2, 0, "sender@example.com", []string{"recipient-2@example.net"})706}