mirror of https://github.com/gophish/gophish
Refactored result updating to be in result.go.
Added the modified_date field to results so it's easy to keep track of the last results that were modified without having to parse every event. Updated the tests to reflect the changes.pull/1090/head
parent
222399c5f6
commit
420410b52c
|
@ -26,4 +26,5 @@ gophish_admin.crt
|
|||
gophish_admin.key
|
||||
|
||||
*.exe
|
||||
*.db
|
||||
gophish.db*
|
||||
gophish
|
|
@ -2,7 +2,6 @@ package controllers
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
@ -26,13 +25,6 @@ var ErrInvalidRequest = errors.New("Invalid request")
|
|||
// has already been marked as complete.
|
||||
var ErrCampaignComplete = errors.New("Event received on completed campaign")
|
||||
|
||||
// eventDetails is a struct that wraps common attributes we want to store
|
||||
// in an event
|
||||
type eventDetails struct {
|
||||
Payload url.Values `json:"payload"`
|
||||
Browser map[string]string `json:"browser"`
|
||||
}
|
||||
|
||||
// CreatePhishingRouter creates the router that handles phishing connections.
|
||||
func CreatePhishingRouter() http.Handler {
|
||||
router := mux.NewRouter()
|
||||
|
@ -59,16 +51,8 @@ func PhishTracker(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
rs := ctx.Get(r, "result").(models.Result)
|
||||
c := ctx.Get(r, "campaign").(models.Campaign)
|
||||
rj := ctx.Get(r, "details").([]byte)
|
||||
c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_OPENED, Details: string(rj)})
|
||||
// Don't update the status if the user already clicked the link
|
||||
// or submitted data to the campaign
|
||||
if rs.Status == models.EVENT_CLICKED || rs.Status == models.EVENT_DATA_SUBMIT {
|
||||
http.ServeFile(w, r, "static/images/pixel.png")
|
||||
return
|
||||
}
|
||||
err = rs.UpdateStatus(models.EVENT_OPENED)
|
||||
d := ctx.Get(r, "details").(models.EventDetails)
|
||||
err = rs.HandleEmailOpened(d)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -87,11 +71,9 @@ func PhishReporter(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
rs := ctx.Get(r, "result").(models.Result)
|
||||
c := ctx.Get(r, "campaign").(models.Campaign)
|
||||
rj := ctx.Get(r, "details").([]byte)
|
||||
c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_REPORTED, Details: string(rj)})
|
||||
d := ctx.Get(r, "details").(models.EventDetails)
|
||||
|
||||
err = rs.UpdateReported(true)
|
||||
err = rs.HandleEmailReport(d)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -112,7 +94,7 @@ func PhishHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
rs := ctx.Get(r, "result").(models.Result)
|
||||
c := ctx.Get(r, "campaign").(models.Campaign)
|
||||
rj := ctx.Get(r, "details").([]byte)
|
||||
d := ctx.Get(r, "details").(models.EventDetails)
|
||||
p, err := models.GetPage(c.PageId, c.UserId)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -121,18 +103,12 @@ func PhishHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
switch {
|
||||
case r.Method == "GET":
|
||||
if rs.Status != models.EVENT_CLICKED && rs.Status != models.EVENT_DATA_SUBMIT {
|
||||
rs.UpdateStatus(models.EVENT_CLICKED)
|
||||
}
|
||||
err = c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_CLICKED, Details: string(rj)})
|
||||
err = rs.HandleClickedLink(d)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
case r.Method == "POST":
|
||||
// If data was POST'ed, let's record it
|
||||
rs.UpdateStatus(models.EVENT_DATA_SUBMIT)
|
||||
// Store the data in an event
|
||||
c.AddEvent(models.Event{Email: rs.Email, Message: models.EVENT_DATA_SUBMIT, Details: string(rj)})
|
||||
err = rs.HandleFormSubmit(d)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -224,16 +200,15 @@ func setupContext(r *http.Request) (error, *http.Request) {
|
|||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
d := eventDetails{
|
||||
d := models.EventDetails{
|
||||
Payload: r.Form,
|
||||
Browser: make(map[string]string),
|
||||
}
|
||||
d.Browser["address"] = ip
|
||||
d.Browser["user-agent"] = r.Header.Get("User-Agent")
|
||||
rj, err := json.Marshal(d)
|
||||
|
||||
r = ctx.Set(r, "result", rs)
|
||||
r = ctx.Set(r, "campaign", c)
|
||||
r = ctx.Set(r, "details", rj)
|
||||
r = ctx.Set(r, "details", d)
|
||||
return nil, r
|
||||
}
|
||||
|
|
|
@ -66,7 +66,10 @@ func (s *ControllersSuite) TestOpenedPhishingEmail() {
|
|||
|
||||
campaign = s.getFirstCampaign()
|
||||
result = campaign.Results[0]
|
||||
lastEvent := campaign.Events[len(campaign.Events)-1]
|
||||
s.Equal(result.Status, models.EVENT_OPENED)
|
||||
s.Equal(lastEvent.Message, models.EVENT_OPENED)
|
||||
s.Equal(result.ModifiedDate, lastEvent.Time)
|
||||
}
|
||||
|
||||
func (s *ControllersSuite) TestReportedPhishingEmail() {
|
||||
|
@ -78,8 +81,10 @@ func (s *ControllersSuite) TestReportedPhishingEmail() {
|
|||
|
||||
campaign = s.getFirstCampaign()
|
||||
result = campaign.Results[0]
|
||||
lastEvent := campaign.Events[len(campaign.Events)-1]
|
||||
s.Equal(result.Reported, true)
|
||||
s.Equal(campaign.Events[len(campaign.Events)-1].Message, models.EVENT_REPORTED)
|
||||
s.Equal(lastEvent.Message, models.EVENT_REPORTED)
|
||||
s.Equal(result.ModifiedDate, lastEvent.Time)
|
||||
}
|
||||
|
||||
func (s *ControllersSuite) TestClickedPhishingLinkAfterOpen() {
|
||||
|
@ -92,7 +97,10 @@ func (s *ControllersSuite) TestClickedPhishingLinkAfterOpen() {
|
|||
|
||||
campaign = s.getFirstCampaign()
|
||||
result = campaign.Results[0]
|
||||
lastEvent := campaign.Events[len(campaign.Events)-1]
|
||||
s.Equal(result.Status, models.EVENT_CLICKED)
|
||||
s.Equal(lastEvent.Message, models.EVENT_CLICKED)
|
||||
s.Equal(result.ModifiedDate, lastEvent.Time)
|
||||
}
|
||||
|
||||
func (s *ControllersSuite) TestNoRecipientID() {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE results ADD COLUMN modified_date DATETIME;
|
||||
|
||||
UPDATE results
|
||||
SET `modified_date`= (
|
||||
SELECT max(events.time) FROM events
|
||||
WHERE events.email=results.email
|
||||
AND events.campaign_id=results.campaign_id
|
||||
);
|
||||
|
||||
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
ALTER TABLE results ADD COLUMN modified_date DATETIME;
|
||||
|
||||
UPDATE results
|
||||
SET `modified_date`= (
|
||||
SELECT max(events.time) FROM events
|
||||
WHERE events.email=results.email
|
||||
AND events.campaign_id=results.campaign_id
|
||||
);
|
||||
|
||||
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
|
@ -2,6 +2,7 @@ package models
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
log "github.com/gophish/gophish/logger"
|
||||
|
@ -68,6 +69,30 @@ type CampaignStats struct {
|
|||
Error int64 `json:"error"`
|
||||
}
|
||||
|
||||
// Event contains the fields for an event
|
||||
// that occurs during the campaign
|
||||
type Event struct {
|
||||
Id int64 `json:"-"`
|
||||
CampaignId int64 `json:"-"`
|
||||
Email string `json:"email"`
|
||||
Time time.Time `json:"time"`
|
||||
Message string `json:"message"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
|
||||
// EventDetails is a struct that wraps common attributes we want to store
|
||||
// in an event
|
||||
type EventDetails struct {
|
||||
Payload url.Values `json:"payload"`
|
||||
Browser map[string]string `json:"browser"`
|
||||
}
|
||||
|
||||
// EventError is a struct that wraps an error that occurs when sending an
|
||||
// email to a recipient
|
||||
type EventError struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// ErrCampaignNameNotSpecified indicates there was no template given by the user
|
||||
var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified")
|
||||
|
||||
|
@ -122,10 +147,10 @@ func (c *Campaign) UpdateStatus(s string) error {
|
|||
}
|
||||
|
||||
// AddEvent creates a new campaign event in the database
|
||||
func (c *Campaign) AddEvent(e Event) error {
|
||||
func (c *Campaign) AddEvent(e *Event) error {
|
||||
e.CampaignId = c.Id
|
||||
e.Time = time.Now().UTC()
|
||||
return db.Save(&e).Error
|
||||
return db.Save(e).Error
|
||||
}
|
||||
|
||||
// getDetails retrieves the related attributes of the campaign
|
||||
|
@ -220,17 +245,6 @@ func getCampaignStats(cid int64) (CampaignStats, error) {
|
|||
return s, err
|
||||
}
|
||||
|
||||
// Event contains the fields for an event
|
||||
// that occurs during the campaign
|
||||
type Event struct {
|
||||
Id int64 `json:"-"`
|
||||
CampaignId int64 `json:"-"`
|
||||
Email string `json:"email"`
|
||||
Time time.Time `json:"time"`
|
||||
Message string `json:"message"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
|
||||
// GetCampaigns returns the campaigns owned by the given user.
|
||||
func GetCampaigns(uid int64) ([]Campaign, error) {
|
||||
cs := []Campaign{}
|
||||
|
@ -422,7 +436,7 @@ func PostCampaign(c *Campaign, uid int64) error {
|
|||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = c.AddEvent(Event{Message: "Campaign Created"})
|
||||
err = c.AddEvent(&Event{Message: "Campaign Created"})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
@ -447,6 +461,7 @@ func PostCampaign(c *Campaign, uid int64) error {
|
|||
LastName: t.LastName,
|
||||
SendDate: c.LaunchDate,
|
||||
Reported: false,
|
||||
ModifiedDate: c.CreatedDate,
|
||||
}
|
||||
if c.Status == CAMPAIGN_IN_PROGRESS {
|
||||
r.Status = STATUS_SENDING
|
||||
|
|
|
@ -3,7 +3,6 @@ package models
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -58,20 +57,16 @@ func GenerateMailLog(c *Campaign, r *Result) error {
|
|||
// too many times. Backoff also unlocks the maillog so that it can be processed
|
||||
// again in the future.
|
||||
func (m *MailLog) Backoff(reason error) error {
|
||||
if m.SendAttempt == MaxSendAttempts {
|
||||
err = m.addError(ErrMaxSendAttempts)
|
||||
return ErrMaxSendAttempts
|
||||
}
|
||||
r, err := GetResult(m.RId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.SendAttempt == MaxSendAttempts {
|
||||
r.HandleEmailError(ErrMaxSendAttempts)
|
||||
return ErrMaxSendAttempts
|
||||
}
|
||||
// Add an error, since we had to backoff because of a
|
||||
// temporary error of some sort during the SMTP transaction
|
||||
err = m.addError(reason)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.SendAttempt++
|
||||
backoffDuration := math.Pow(2, float64(m.SendAttempt))
|
||||
m.SendDate = m.SendDate.Add(time.Minute * time.Duration(backoffDuration))
|
||||
|
@ -79,9 +74,7 @@ func (m *MailLog) Backoff(reason error) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Status = STATUS_RETRY
|
||||
r.SendDate = m.SendDate
|
||||
err = db.Save(r).Error
|
||||
err = r.HandleEmailBackoff(reason, m.SendDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -101,32 +94,6 @@ func (m *MailLog) Lock() error {
|
|||
return db.Save(&m).Error
|
||||
}
|
||||
|
||||
// addError adds an error to the associated campaign
|
||||
func (m *MailLog) addError(e error) error {
|
||||
c, err := GetCampaign(m.CampaignId, m.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// This is redundant in the case of permanent
|
||||
// errors, but the extra query makes for
|
||||
// a cleaner API.
|
||||
r, err := GetResult(m.RId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
es := struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: e.Error(),
|
||||
}
|
||||
ej, err := json.Marshal(es)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
err = c.AddEvent(Event{Email: r.Email, Message: EVENT_SENDING_ERROR, Details: string(ej)})
|
||||
return err
|
||||
}
|
||||
|
||||
// Error sets the error status on the models.Result that the
|
||||
// maillog refers to. Since MailLog errors are permanent,
|
||||
// this action also deletes the maillog.
|
||||
|
@ -136,14 +103,7 @@ func (m *MailLog) Error(e error) error {
|
|||
log.Warn(err)
|
||||
return err
|
||||
}
|
||||
// Update the result
|
||||
err = r.UpdateStatus(ERROR)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return err
|
||||
}
|
||||
// Update the campaign events
|
||||
err = m.addError(e)
|
||||
err = r.HandleEmailError(e)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return err
|
||||
|
@ -159,15 +119,7 @@ func (m *MailLog) Success() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.UpdateStatus(EVENT_SENT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := GetCampaign(m.CampaignId, m.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.AddEvent(Event{Email: r.Email, Message: EVENT_SENT})
|
||||
err = r.HandleEmailSent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -105,11 +105,7 @@ func (s *ModelsSuite) TestMailLogError(ch *check.C) {
|
|||
ch.Assert(len(campaign.Events), check.Equals, expectedEventLength)
|
||||
|
||||
gotEvent := campaign.Events[1]
|
||||
es := struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: expectedError.Error(),
|
||||
}
|
||||
es := EventError{Error: expectedError.Error()}
|
||||
ej, _ := json.Marshal(es)
|
||||
expectedEvent := Event{
|
||||
Id: gotEvent.Id,
|
||||
|
|
125
models/result.go
125
models/result.go
|
@ -2,6 +2,7 @@ package models
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
|
@ -39,16 +40,119 @@ type Result struct {
|
|||
Longitude float64 `json:"longitude"`
|
||||
SendDate time.Time `json:"send_date"`
|
||||
Reported bool `json:"reported" sql:"not null"`
|
||||
ModifiedDate time.Time `json:"modified_date"`
|
||||
}
|
||||
|
||||
// UpdateStatus updates the status of the result in the database
|
||||
func (r *Result) UpdateStatus(s string) error {
|
||||
return db.Table("results").Where("id=?", r.Id).Update("status", s).Error
|
||||
func (r *Result) createEvent(status string, details interface{}) (*Event, error) {
|
||||
c, err := GetCampaign(r.CampaignId, r.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e := &Event{Email: r.Email, Message: status}
|
||||
if details != nil {
|
||||
dj, err := json.Marshal(details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Details = string(dj)
|
||||
}
|
||||
c.AddEvent(e)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// UpdateReported updates when a user reports a campaign
|
||||
func (r *Result) UpdateReported(s bool) error {
|
||||
return db.Table("results").Where("id=?", r.Id).Update("reported", s).Error
|
||||
// HandleEmailSent updates a Result to indicate that the email has been
|
||||
// successfully sent to the remote SMTP server
|
||||
func (r *Result) HandleEmailSent() error {
|
||||
event, err := r.createEvent(EVENT_SENT, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Status = EVENT_SENT
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleEmailError updates a Result to indicate that there was an error when
|
||||
// attempting to send the email to the remote SMTP server.
|
||||
func (r *Result) HandleEmailError(err error) error {
|
||||
event, err := r.createEvent(EVENT_SENDING_ERROR, EventError{Error: err.Error()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Status = ERROR
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleEmailBackoff updates a Result to indicate that the email received a
|
||||
// temporary error and needs to be retried
|
||||
func (r *Result) HandleEmailBackoff(err error, sendDate time.Time) error {
|
||||
event, err := r.createEvent(EVENT_SENDING_ERROR, EventError{Error: err.Error()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Status = STATUS_RETRY
|
||||
r.SendDate = sendDate
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleEmailOpened updates a Result in the case where the recipient opened the
|
||||
// email.
|
||||
func (r *Result) HandleEmailOpened(details EventDetails) error {
|
||||
event, err := r.createEvent(EVENT_OPENED, details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Don't update the status if the user already clicked the link
|
||||
// or submitted data to the campaign
|
||||
if r.Status == EVENT_CLICKED || r.Status == EVENT_DATA_SUBMIT {
|
||||
return nil
|
||||
}
|
||||
r.Status = EVENT_OPENED
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleClickedLink updates a Result in the case where the recipient clicked
|
||||
// the link in an email.
|
||||
func (r *Result) HandleClickedLink(details EventDetails) error {
|
||||
event, err := r.createEvent(EVENT_CLICKED, details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Don't update the status if the user has already submitted data via the
|
||||
// landing page form.
|
||||
if r.Status == EVENT_DATA_SUBMIT {
|
||||
return nil
|
||||
}
|
||||
r.Status = EVENT_CLICKED
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleFormSubmit updates a Result in the case where the recipient submitted
|
||||
// credentials to the form on a Landing Page.
|
||||
func (r *Result) HandleFormSubmit(details EventDetails) error {
|
||||
event, err := r.createEvent(EVENT_DATA_SUBMIT, details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Status = EVENT_DATA_SUBMIT
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// HandleEmailReport updates a Result in the case where they report a simulated
|
||||
// phishing email using the HTTP handler.
|
||||
func (r *Result) HandleEmailReport(details EventDetails) error {
|
||||
event, err := r.createEvent(EVENT_REPORTED, details)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Reported = true
|
||||
r.ModifiedDate = event.Time
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// UpdateGeo updates the latitude and longitude of the result in
|
||||
|
@ -68,11 +172,10 @@ func (r *Result) UpdateGeo(addr string) error {
|
|||
return err
|
||||
}
|
||||
// Update the database with the record information
|
||||
return db.Table("results").Where("id=?", r.Id).Updates(map[string]interface{}{
|
||||
"ip": addr,
|
||||
"latitude": city.GeoPoint.Latitude,
|
||||
"longitude": city.GeoPoint.Longitude,
|
||||
}).Error
|
||||
r.IP = addr
|
||||
r.Latitude = city.GeoPoint.Latitude
|
||||
r.Longitude = city.GeoPoint.Longitude
|
||||
return db.Save(r).Error
|
||||
}
|
||||
|
||||
// GenerateId generates a unique key to represent the result
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"regexp"
|
||||
"time"
|
||||
|
@ -40,10 +39,9 @@ func (s *ModelsSuite) TestResultSendingStatus(ch *check.C) {
|
|||
ch.Assert(PostCampaign(&c, c.UserId), check.Equals, nil)
|
||||
// This campaign wasn't scheduled, so we expect the status to
|
||||
// be sending
|
||||
fmt.Println("Campaign STATUS")
|
||||
fmt.Println(c.Status)
|
||||
for _, r := range c.Results {
|
||||
ch.Assert(r.Status, check.Equals, STATUS_SENDING)
|
||||
ch.Assert(r.ModifiedDate, check.Equals, c.CreatedDate)
|
||||
}
|
||||
}
|
||||
func (s *ModelsSuite) TestResultScheduledStatus(ch *check.C) {
|
||||
|
@ -54,6 +52,7 @@ func (s *ModelsSuite) TestResultScheduledStatus(ch *check.C) {
|
|||
// be sending
|
||||
for _, r := range c.Results {
|
||||
ch.Assert(r.Status, check.Equals, STATUS_SCHEDULED)
|
||||
ch.Assert(r.ModifiedDate, check.Equals, c.CreatedDate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue