Repository: kubernetes
Issue: Better HPA unit and E2E test coverage
Labels: sig/autoscaling,kind/feature,triage/accepted

### What would you like to be added?

The HPA needs better unit and E2E test coverage.

**E2E TESTS**

Missing E2E test coverage by API / feature
    
Spec.ScaleTargetRef
- [x] scaling a CRD (not built-in kind)

Spec.MinReplicas
- [x] scale-to-zero (minReplicas=0)

Spec.Metrics
- [ ] scale on cpu average value
- [ ] scale on memory average value
- [ ] scale on memory utilization
- [ ] scale on multiple metric types (resource, container resource, pods, object, external)
  - [ ] scale up when one metric missing
  - [ ] no scale down when one metric missing

Spec.Behavior
- [x] scale up stabilization window
- [x] scale and and down controls (see #97348)
- [x] scaling disabled up / down (select policy disabled)
- [x] scale up / down rate / pods

**UNIT TESTS**

Missing unit test coverage by branch / case.

**horizontal.go**

deleteHPA()
- [ ] deleted hpa

computeReplicasForMetric()
- [ ] failed to get object metric value
- [ ] failed to get pods metriv value
- [ ] err on get container resource metric status

reconcileKey()
- [ ] hpa not found (deleted)

computeStatusForObjectMetric()
- [ ] error getting average value object metric

computeStatusForResourceMetricGeneric()
- [ ] average value
- [ ] neither utilization or average value

computeStatusForContainerResourceMetric()
- [ ] FailedGetContainerResourceMetric

FailedGetContainerResourceMetric()   
- [ ] FailedGetExternalMetric

reconcileAutoscaler()
- [ ] default min replica = 1      
- [ ] error updating status after error getting recommendation
- [ ] error updating status after error updates scale subresource  

normalizeDesiredReplicasWithBehaviors()
- [ ] stabilizedRecommendation != prenormalizedDesiredReplicas scale up/down stabilized

calculateScaleUpLimitWithScalingRules()
- [ ] scaling disabled      

calculateScaleDownLimitWithBehaviors()
- [ ] scaling disabled

scaleForResourceMappings()
- [ ] unrecognized resource

updateStatus()
- [ ] error updating status      
  
**metrics/client.go**

GetResourceMetric()
- [ ] error fetching metrics

getPodMetrics()
- [ ] missing resource metrics

GetRawMetric()
- [ ] unable to fetch metrics
- [ ] metric window seconds != nil

GetObjectMetric()
- [ ] kind=namespace object metric (root scoped)
- [ ] error getting metrics

GetExternalMetric()
- [ ] error getting metrics      

**replica_calculator.go**

GetResourceReplicas()
- [ ] error listing pods
- [ ] empty list of metrics
- [ ] second run of get resource utilization ratio

GetRawResourceReplicas()
- [ ] whole function uncovered

calcPlainMetricReplicas()
- [ ] error list pods
- [ ] empty list list of pods
- [ ] empty list of metrics
- [ ] missing metrics and wanting to scale up (treat missing as 0%)
- [ ] rebalance ignored (treat unready as 0%)
- [ ] no scale because within tolerance
- [ ] normal return of new replicas (last line)

getUsageRatioReplicaCount()
- [ ] error calling get ready pods count

GetObjectPerPodMetricReplicas()
- [ ] error calling get object metric

getReadyPodsCount()
- [ ] error calling list pods
- [ ] pod list empty

GetExternalMetricReplicas()
- [ ] error parsing metric label selector

GetExternalPerPodMetricReplicas()
- [ ] error parsing metric label selector
- [ ] error calling get external metric

### Why is this needed?

The more coverage to less likely we are to break something when refactoring / adding features / fixing bugs.