func (da *RedisDataAccess) DebitIfNotZero(accountKey string, amount int64, dailyBudget int64, dailyBudgetExpiration time.Time) (remainingDailyBudgetInMicroCents int64, err error) { client, err := da.pool.Get() if err != nil { panic(err) } defer da.pool.CarefullyPut(client, &err) accountKeyWithDomain := da.withDomain(accountKey) debitIfNotZeroSha := da.getDebitIfNotZeroSha() dailyBudgetSha := da.getDailyBudgetSha() client.Append("MULTI") client.Append("EVALSHA", dailyBudgetSha, 1, accountKeyWithDomain, dailyBudget, dailyBudgetExpiration.Unix()) client.Append("EVALSHA", debitIfNotZeroSha, 1, accountKeyWithDomain, amount) client.Append("GET", accountKeyWithDomain) client.Append("EXEC") client.GetReply() client.GetReply() client.GetReply() client.GetReply() transaction := client.GetReply() if transaction.Err != nil { return 0, rtb.NewTransactionError(transaction.Err.Error(), false) } if err != nil { return 0, rtb.NewTransactionError(err.Error(), false) } amountValid := transaction.Elems[2].Type != redis.NilReply && transaction.Elems[2].Err == nil if amountValid { remainingDailyBudgetInMicroCents, _ = transaction.Elems[2].Int64() } if transaction.Elems[1].Type == redis.NilReply { return remainingDailyBudgetInMicroCents, rtb.NewTransactionError("Insufficient daily funds.", amountValid) } else { return remainingDailyBudgetInMicroCents, nil } }
// TestBiddingMatchedTarget tests that the bidder will bid on the first campaign returned by the bidder that has a remaining daily budget // Expected result is a bid response that is populated by a single bid with a campaign ID matching the first campaigned returned by the provider func TestBiddingFirstCampaignOutOfFunds(t *testing.T) { r := new(rtb.BidRequest) r.Imp = make([]rtb.Imp, 1) r.Imp[0] = rtb.Imp{} pacer := mocks.NewMockPacer(map[int64]bool{100: true, 101: true}) campaign1 := mocks.NewMockCampaign(100, rtb.CpmToMicroCents(0.25), rtb.DollarsToMicroCents(1), nil) campaign2 := mocks.NewMockCampaign(101, rtb.CpmToMicroCents(0.25), rtb.DollarsToMicroCents(1), nil) campaignsReturnedByTargeting := []rtb.Campaign{campaign1, campaign2} cp := mocks.NewMockCampaignProvider(campaignsReturnedByTargeting, map[int64]int64{100: rtb.DollarsToMicroCents(1), 101: rtb.DollarsToMicroCents(1)}, map[int64]error{100: rtb.NewTransactionError("test", true), 101: nil}, nil) b := NewBidRequestBidder(r, cp, pacer, time.Now().UTC()) response, _, err := b.Bid() if err != nil { t.Fail() } if response == nil { t.FailNow() } if response.Seatbid == nil { t.FailNow() } if len(response.Seatbid) != 1 { t.Fail() } if response.Seatbid[0].Bid == nil { t.FailNow() } if len(response.Seatbid[0].Bid) != 1 { t.FailNow() } if response.Seatbid[0].Bid[0].Cid != strconv.FormatInt(campaignsReturnedByTargeting[1].Id(), 10) { t.Fail() } }
// TestBiddingMatchedTarget tests that the bidder will not bid if a campaign is out of funds // Expected result is nil func TestBiddingMatchedTargetNoFundsAvailable(t *testing.T) { r := new(rtb.BidRequest) r.Imp = make([]rtb.Imp, 1) r.Imp[0] = rtb.Imp{} pacer := mocks.NewMockPacer(map[int64]bool{100: true}) campaign := mocks.NewMockCampaign(100, rtb.CpmToMicroCents(0.25), rtb.DollarsToMicroCents(1), nil) campaignsReturnedByTargeting := []rtb.Campaign{campaign} cp := mocks.NewMockCampaignProvider(campaignsReturnedByTargeting, map[int64]int64{100: rtb.DollarsToMicroCents(1)}, map[int64]error{100: rtb.NewTransactionError("error", true)}, nil) b := NewBidRequestBidder(r, cp, pacer, time.Now().UTC()) response, _, err := b.Bid() if err != nil { t.Fail() } if response != nil { t.FailNow() } }