apf: rename WorkEstimate.Seats to InitialSeats

Kubernetes-commit: 5d67896adedbce27f01b59eb5f2054919a047f2b
This commit is contained in:
Abu Kashem 2021-09-24 09:41:38 -04:00 committed by Kubernetes Publisher
parent e75fd4b307
commit 863c48fbc2
8 changed files with 75 additions and 75 deletions

View File

@ -71,7 +71,7 @@ const (
)
var defaultRequestWorkEstimator = func(*http.Request) fcrequest.WorkEstimate {
return fcrequest.WorkEstimate{Seats: 1}
return fcrequest.WorkEstimate{InitialSeats: 1}
}
type fakeApfFilter struct {
@ -653,7 +653,7 @@ func TestApfWithRequestDigest(t *testing.T) {
RequestInfo: &apirequest.RequestInfo{Verb: "get"},
User: &user.DefaultInfo{Name: "foo"},
WorkEstimate: fcrequest.WorkEstimate{
Seats: 5,
InitialSeats: 5,
},
}

View File

@ -154,7 +154,7 @@ func TestFIFOSeatsSum(t *testing.T) {
list := newRequestFIFO()
newRequest := func(width uint) *request {
return &request{workEstimate: fcrequest.WorkEstimate{Seats: width}}
return &request{workEstimate: fcrequest.WorkEstimate{InitialSeats: width}}
}
arrival := []*request{newRequest(1), newRequest(2), newRequest(3)}
removeFn := make([]removeFromFIFOFunc, 0)

View File

@ -268,7 +268,7 @@ func (qs *queueSet) StartRequest(ctx context.Context, workEstimate *fqrequest.Wo
// Step 0:
// Apply only concurrency limit, if zero queues desired
if qs.qCfg.DesiredNumQueues < 1 {
if !qs.canAccommodateSeatsLocked(int(workEstimate.Seats)) {
if !qs.canAccommodateSeatsLocked(int(workEstimate.InitialSeats)) {
klog.V(5).Infof("QS(%s): rejecting request %q %#+v %#+v because %d seats are asked for, %d seats are in use (%d are executing) and the limit is %d",
qs.qCfg.Name, fsName, descr1, descr2, workEstimate, qs.totSeatsInUse, qs.totRequestsExecuting, qs.dCfg.ConcurrencyLimit)
metrics.AddReject(ctx, qs.qCfg.Name, fsName, "concurrency-limit")
@ -317,7 +317,7 @@ func ordinaryPromiseFactoryFactory(qs *queueSet) promiseFactory {
// Seats returns the number of seats this request requires.
func (req *request) Seats() int {
return int(req.workEstimate.Seats)
return int(req.workEstimate.InitialSeats)
}
func (req *request) NoteQueued(inQueue bool) {
@ -795,10 +795,10 @@ func (qs *queueSet) finishRequestLocked(r *request) {
if !klog.V(6).Enabled() {
} else if r.queue != nil {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished all use of %d seats, adjusted queue %d start R to %.9fss due to service time %.9fs, queue will have %d requests, %d seats waiting & %d requests occupying %d seats",
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, r.queue.index,
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, r.queue.index,
r.queue.virtualStart, S, r.queue.requests.Length(), r.queue.requests.SeatsSum(), r.queue.requestsExecuting, r.queue.seatsInUse)
} else {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished all use of %d seats, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, qs.totRequestsExecuting, qs.totSeatsInUse)
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished all use of %d seats, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, qs.totRequestsExecuting, qs.totSeatsInUse)
}
return
}
@ -807,10 +807,10 @@ func (qs *queueSet) finishRequestLocked(r *request) {
if !klog.V(6).Enabled() {
} else if r.queue != nil {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished main use but lingering on %d seats for %v seconds, adjusted queue %d start R to %.9fss due to service time %.9fs, queue will have %d requests waiting & %d executing, still has %d seats waiting & %d executing",
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, additionalLatency.Seconds(), r.queue.index,
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, additionalLatency.Seconds(), r.queue.index,
r.queue.virtualStart, S, r.queue.requests.Length(), r.queue.requestsExecuting, r.queue.requests.SeatsSum(), r.queue.seatsInUse)
} else {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished main use but lingering on %d seats for %v seconds, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, additionalLatency.Seconds(), qs.totRequestsExecuting, qs.totSeatsInUse)
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished main use but lingering on %d seats for %v seconds, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, additionalLatency.Seconds(), qs.totRequestsExecuting, qs.totSeatsInUse)
}
// EventAfterDuration will execute the event func in a new goroutine,
// so the seats allocated to this request will be released after
@ -824,10 +824,10 @@ func (qs *queueSet) finishRequestLocked(r *request) {
if !klog.V(6).Enabled() {
} else if r.queue != nil {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished lingering on %d seats, queue %d will have %d requests, %d seats waiting & %d requests occupying %d seats",
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, r.queue.index,
qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, r.queue.index,
r.queue.requests.Length(), r.queue.requests.SeatsSum(), r.queue.requestsExecuting, r.queue.seatsInUse)
} else {
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished lingering on %d seats, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.Seats, qs.totRequestsExecuting, qs.totSeatsInUse)
klog.Infof("QS(%s) at r=%s v=%.9fss: request %#+v %#+v finished lingering on %d seats, qs will have %d requests occupying %d seats", qs.qCfg.Name, now.Format(nsTimeFmt), qs.virtualTime, r.descr1, r.descr2, r.workEstimate.InitialSeats, qs.totRequestsExecuting, qs.totSeatsInUse)
}
qs.dispatchAsMuchAsPossibleLocked()
}, additionalLatency)

View File

@ -266,7 +266,7 @@ func (ust *uniformScenarioThread) callK(k int) {
if k >= ust.nCalls {
return
}
req, idle := ust.uss.qs.StartRequest(context.Background(), &fcrequest.WorkEstimate{Seats: ust.uc.width, AdditionalLatency: ust.uc.padDuration}, ust.uc.hash, "", ust.fsName, ust.uss.name, []int{ust.i, ust.j, k}, nil)
req, idle := ust.uss.qs.StartRequest(context.Background(), &fcrequest.WorkEstimate{InitialSeats: ust.uc.width, AdditionalLatency: ust.uc.padDuration}, ust.uc.hash, "", ust.fsName, ust.uss.name, []int{ust.i, ust.j, k}, nil)
ust.uss.t.Logf("%s: %d, %d, %d got req=%p, idle=%v", ust.uss.clk.Now().Format(nsTimeFmt), ust.i, ust.j, k, req, idle)
if req == nil {
atomic.AddUint64(&ust.uss.failedCount, 1)
@ -945,7 +945,7 @@ func TestContextCancel(t *testing.T) {
expectQNCount(fn, false, expectF)
expectQNCount(fn, true, expectT)
}
req1, _ := qs.StartRequest(ctx1, &fcrequest.WorkEstimate{Seats: 1}, 1, "", "fs1", "test", "one", queueNoteFn(1))
req1, _ := qs.StartRequest(ctx1, &fcrequest.WorkEstimate{InitialSeats: 1}, 1, "", "fs1", "test", "one", queueNoteFn(1))
if req1 == nil {
t.Error("Request rejected")
return
@ -968,7 +968,7 @@ func TestContextCancel(t *testing.T) {
counter.Add(1)
cancel2()
}()
req2, idle2a := qs.StartRequest(ctx2, &fcrequest.WorkEstimate{Seats: 1}, 2, "", "fs2", "test", "two", queueNoteFn(2))
req2, idle2a := qs.StartRequest(ctx2, &fcrequest.WorkEstimate{InitialSeats: 1}, 2, "", "fs2", "test", "two", queueNoteFn(2))
if idle2a {
t.Error("2nd StartRequest returned idle")
}
@ -1041,7 +1041,7 @@ func TestTotalRequestsExecutingWithPanic(t *testing.T) {
}
ctx := context.Background()
req, _ := qs.StartRequest(ctx, &fcrequest.WorkEstimate{Seats: 1}, 1, "", "fs", "test", "one", func(inQueue bool) {})
req, _ := qs.StartRequest(ctx, &fcrequest.WorkEstimate{InitialSeats: 1}, 1, "", "fs", "test", "one", func(inQueue bool) {})
if req == nil {
t.Fatal("expected a Request object from StartRequest, but got nil")
}
@ -1094,13 +1094,13 @@ func TestFindDispatchQueueLocked(t *testing.T) {
{
virtualStart: 200,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 1}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 1}},
),
},
{
virtualStart: 100,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 1}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 1}},
),
},
},
@ -1117,7 +1117,7 @@ func TestFindDispatchQueueLocked(t *testing.T) {
{
virtualStart: 200,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 1}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 1}},
),
},
},
@ -1134,13 +1134,13 @@ func TestFindDispatchQueueLocked(t *testing.T) {
{
virtualStart: 200,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 50}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 50}},
),
},
{
virtualStart: 100,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 25}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 25}},
),
},
},
@ -1157,13 +1157,13 @@ func TestFindDispatchQueueLocked(t *testing.T) {
{
virtualStart: 200,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 10}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 10}},
),
},
{
virtualStart: 100,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 25}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 25}},
),
},
},
@ -1180,13 +1180,13 @@ func TestFindDispatchQueueLocked(t *testing.T) {
{
virtualStart: 200,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 10}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 10}},
),
},
{
virtualStart: 100,
requests: newFIFO(
&request{workEstimate: fcrequest.WorkEstimate{Seats: 25}},
&request{workEstimate: fcrequest.WorkEstimate{InitialSeats: 25}},
),
},
},
@ -1249,14 +1249,14 @@ func TestFinishRequestLocked(t *testing.T) {
{
name: "request has additional latency",
workEstimate: fcrequest.WorkEstimate{
Seats: 10,
InitialSeats: 10,
AdditionalLatency: time.Minute,
},
},
{
name: "request has no additional latency",
workEstimate: fcrequest.WorkEstimate{
Seats: 10,
InitialSeats: 10,
},
},
}
@ -1288,9 +1288,9 @@ func TestFinishRequestLocked(t *testing.T) {
var (
queuesetTotalRequestsExecutingExpected = qs.totRequestsExecuting - 1
queuesetTotalSeatsInUseExpected = qs.totSeatsInUse - int(test.workEstimate.Seats)
queuesetTotalSeatsInUseExpected = qs.totSeatsInUse - int(test.workEstimate.InitialSeats)
queueRequestsExecutingExpected = queue.requestsExecuting - 1
queueSeatsInUseExpected = queue.seatsInUse - int(test.workEstimate.Seats)
queueSeatsInUseExpected = queue.seatsInUse - int(test.workEstimate.InitialSeats)
)
qs.finishRequestLocked(r)

View File

@ -103,7 +103,7 @@ func TestLiterals(t *testing.T) {
Parts: []string{"goodrscs", "eman"},
},
User: ui,
WorkEstimate: fcrequest.WorkEstimate{Seats: 1},
WorkEstimate: fcrequest.WorkEstimate{InitialSeats: 1},
}
reqRU := RequestDigest{
RequestInfo: &request.RequestInfo{
@ -119,7 +119,7 @@ func TestLiterals(t *testing.T) {
Parts: []string{"goodrscs", "eman"},
},
User: ui,
WorkEstimate: fcrequest.WorkEstimate{Seats: 1},
WorkEstimate: fcrequest.WorkEstimate{InitialSeats: 1},
}
reqN := RequestDigest{
RequestInfo: &request.RequestInfo{
@ -128,7 +128,7 @@ func TestLiterals(t *testing.T) {
Verb: "goodverb",
},
User: ui,
WorkEstimate: fcrequest.WorkEstimate{Seats: 1},
WorkEstimate: fcrequest.WorkEstimate{InitialSeats: 1},
}
checkRules(t, true, reqRN, []flowcontrol.PolicyRulesWithSubjects{{
Subjects: []flowcontrol.Subject{{Kind: flowcontrol.SubjectKindUser,

View File

@ -45,7 +45,7 @@ func (e *listWorkEstimator) estimate(r *http.Request) WorkEstimate {
if !ok {
// no RequestInfo should never happen, but to be on the safe side
// let's return maximumSeats
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
}
query := r.URL.Query()
@ -55,7 +55,7 @@ func (e *listWorkEstimator) estimate(r *http.Request) WorkEstimate {
// This request is destined to fail in the validation layer,
// return maximumSeats for this request to be consistent.
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
}
isListFromCache := !shouldListFromStorage(query, &listOptions)
@ -66,7 +66,7 @@ func (e *listWorkEstimator) estimate(r *http.Request) WorkEstimate {
// be conservative here and allocate maximum seats to this list request.
// NOTE: if a CRD is removed, its count will go stale first and then the
// pruner will eventually remove the CRD from the cache.
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
case err == ObjectCountNotFoundErr:
// there are two scenarios in which we can see this error:
// a. the type is truly unknown, a typo on the caller's part.
@ -75,11 +75,11 @@ func (e *listWorkEstimator) estimate(r *http.Request) WorkEstimate {
// we don't have a way to distinguish between a and b. b seems to indicate
// to a more severe case of degradation, although b can naturally trigger
// when a CRD is removed. let's be conservative and allocate maximum seats.
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
case err != nil:
// we should never be here since Get returns either ObjectCountStaleErr or
// ObjectCountNotFoundErr, return maximumSeats to be on the safe side.
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
}
limit := numStored
@ -114,7 +114,7 @@ func (e *listWorkEstimator) estimate(r *http.Request) WorkEstimate {
if seats > maximumSeats {
seats = maximumSeats
}
return WorkEstimate{Seats: seats}
return WorkEstimate{InitialSeats: seats}
}
func key(requestInfo *apirequest.RequestInfo) string {

View File

@ -34,8 +34,8 @@ const (
)
type WorkEstimate struct {
// Seats represents the number of seats associated with this request
Seats uint
// InitialSeats represents the number of initial seats associated with this request
InitialSeats uint
// AdditionalLatency specifies the additional duration the seats allocated
// to this request must be reserved after the given request had finished.
@ -77,7 +77,7 @@ func (e *workEstimator) estimate(r *http.Request) WorkEstimate {
if !ok {
klog.ErrorS(fmt.Errorf("no RequestInfo found in context"), "Failed to estimate work for the request", "URI", r.RequestURI)
// no RequestInfo should never happen, but to be on the safe side let's return maximumSeats
return WorkEstimate{Seats: maximumSeats}
return WorkEstimate{InitialSeats: maximumSeats}
}
switch requestInfo.Verb {
@ -85,5 +85,5 @@ func (e *workEstimator) estimate(r *http.Request) WorkEstimate {
return e.listWorkEstimator.EstimateWork(r)
}
return WorkEstimate{Seats: minimumSeats}
return WorkEstimate{InitialSeats: minimumSeats}
}

View File

@ -26,18 +26,18 @@ import (
func TestWorkEstimator(t *testing.T) {
tests := []struct {
name string
requestURI string
requestInfo *apirequest.RequestInfo
counts map[string]int64
countErr error
seatsExpected uint
name string
requestURI string
requestInfo *apirequest.RequestInfo
counts map[string]int64
countErr error
initialSeatsExpected uint
}{
{
name: "request has no RequestInfo",
requestURI: "http://server/apis/",
requestInfo: nil,
seatsExpected: maximumSeats,
name: "request has no RequestInfo",
requestURI: "http://server/apis/",
requestInfo: nil,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is not list",
@ -45,7 +45,7 @@ func TestWorkEstimator(t *testing.T) {
requestInfo: &apirequest.RequestInfo{
Verb: "get",
},
seatsExpected: minimumSeats,
initialSeatsExpected: minimumSeats,
},
{
name: "request verb is list, conversion to ListOptions returns error",
@ -58,7 +58,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 799,
},
seatsExpected: maximumSeats,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, has limit and resource version is 1",
@ -71,7 +71,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 699,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, limit not set",
@ -84,7 +84,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 699,
},
seatsExpected: 7,
initialSeatsExpected: 7,
},
{
name: "request verb is list, resource version not set",
@ -97,7 +97,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 699,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, no query parameters, count known",
@ -110,7 +110,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 399,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, no query parameters, count not known",
@ -120,8 +120,8 @@ func TestWorkEstimator(t *testing.T) {
APIGroup: "foo.bar",
Resource: "events",
},
countErr: ObjectCountNotFoundErr,
seatsExpected: maximumSeats,
countErr: ObjectCountNotFoundErr,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, continuation is set",
@ -134,7 +134,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 699,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, resource version is zero",
@ -147,7 +147,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 399,
},
seatsExpected: 4,
initialSeatsExpected: 4,
},
{
name: "request verb is list, resource version is zero, no limit",
@ -160,7 +160,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 799,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, resource version match is Exact",
@ -173,7 +173,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 699,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, resource version match is NotOlderThan, limit not specified",
@ -186,7 +186,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 799,
},
seatsExpected: 8,
initialSeatsExpected: 8,
},
{
name: "request verb is list, maximum is capped",
@ -199,7 +199,7 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 1999,
},
seatsExpected: maximumSeats,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, list from cache, count not known",
@ -209,8 +209,8 @@ func TestWorkEstimator(t *testing.T) {
APIGroup: "foo.bar",
Resource: "events",
},
countErr: ObjectCountNotFoundErr,
seatsExpected: maximumSeats,
countErr: ObjectCountNotFoundErr,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, object count is stale",
@ -223,8 +223,8 @@ func TestWorkEstimator(t *testing.T) {
counts: map[string]int64{
"events.foo.bar": 799,
},
countErr: ObjectCountStaleErr,
seatsExpected: maximumSeats,
countErr: ObjectCountStaleErr,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, object count is not found",
@ -234,8 +234,8 @@ func TestWorkEstimator(t *testing.T) {
APIGroup: "foo.bar",
Resource: "events",
},
countErr: ObjectCountNotFoundErr,
seatsExpected: maximumSeats,
countErr: ObjectCountNotFoundErr,
initialSeatsExpected: maximumSeats,
},
{
name: "request verb is list, count getter throws unknown error",
@ -245,8 +245,8 @@ func TestWorkEstimator(t *testing.T) {
APIGroup: "foo.bar",
Resource: "events",
},
countErr: errors.New("unknown error"),
seatsExpected: maximumSeats,
countErr: errors.New("unknown error"),
initialSeatsExpected: maximumSeats,
},
}
@ -271,8 +271,8 @@ func TestWorkEstimator(t *testing.T) {
}
workestimateGot := estimator.EstimateWork(req)
if test.seatsExpected != workestimateGot.Seats {
t.Errorf("Expected work estimate to match: %d seats, but got: %d seats", test.seatsExpected, workestimateGot.Seats)
if test.initialSeatsExpected != workestimateGot.InitialSeats {
t.Errorf("Expected work estimate to match: %d seats, but got: %d seats", test.initialSeatsExpected, workestimateGot.InitialSeats)
}
})
}