Repository: PrefectHQ/prefect

Issue:
StateGroup.all_final() is looking at non-final states and not final states.
### First check

- [X] I added a descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Prefect documentation for this issue.
- [X] I checked that this issue is related to Prefect and not one of its dependencies.

### Bug summary

`StateGroup.all_final()` is using the count of not-final states rather than the count of final states resulting in incorrect messaging when a Flow finishes with all final states.

### Reproduction

```python3
prefect import flow, task
from prefect.orion.schemas.states import Cancelled


@task
def skippy(x: bool = False):
    return Cancelled(message="Skip it!")


@flow
def my_flow():
    return skippy()


if __name__ == "__main__":
    my_flow()


The end state of the flow is `Failed('0/1 states are not final.')` but `Cancelled` is a final state so this should be `Failed("Given states: total=1, 'CANCELLED'=1")`
```


### Error

_No response_

### Versions

```Text
Latest `main`
```


### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/src/prefect/states.py b/src/prefect/states.py
--- a/src/prefect/states.py
+++ b/src/prefect/states.py
@@ -371,7 +371,8 @@ def __init__(self, states: Iterable[State]) -> None:
         self.states = states
         self.type_counts = self._get_type_counts(states)
         self.total_count = len(states)
-        self.not_final_count = self._get_not_final_count(states)
+        self.final_count = sum(state.is_final() for state in states)
+        self.not_final_count = self.total_count - self.final_count
 
     @property
     def fail_count(self):
@@ -387,25 +388,25 @@ def any_failed(self) -> bool:
         )
 
     def all_final(self) -> bool:
-        return self.not_final_count == self.total_count
+        return self.final_count == self.total_count
 
     def counts_message(self) -> str:
         count_messages = [f"total={self.total_count}"]
         if self.not_final_count:
             count_messages.append(f"not_final={self.not_final_count}")
-        for state_type, count in self.type_counts.items():
-            if count:
-                count_messages.append(f"{state_type.value!r}={count}")
+
+        count_messages += [
+            f"{state_type.value!r}={count}"
+            for state_type, count in self.type_counts.items()
+            if count
+        ]
+
         return ", ".join(count_messages)
 
     @staticmethod
     def _get_type_counts(states: Iterable[State]) -> Dict[StateType, int]:
         return Counter(state.type for state in states)
 
-    @staticmethod
-    def _get_not_final_count(states: Iterable[State]) -> int:
-        return len(states) - sum(state.is_final() for state in states)
-
     def __repr__(self) -> str:
         return f"StateGroup<{self.counts_message()}>"
 


Test patch:
diff --git a/tests/test_states.py b/tests/test_states.py
--- a/tests/test_states.py
+++ b/tests/test_states.py
@@ -5,11 +5,13 @@
 from prefect.exceptions import CrashedRun, FailedRun
 from prefect.results import LiteralResult, PersistedResult, ResultFactory
 from prefect.states import (
+    Cancelled,
     Completed,
     Crashed,
     Failed,
     Pending,
     Running,
+    StateGroup,
     is_state,
     is_state_iterable,
     raise_state_exception,
@@ -201,3 +203,100 @@ async def test_non_prefect_types_return_completed_state(self, factory):
         result_state = await return_value_to_state("foo", factory)
         assert result_state.is_completed()
         assert await result_state.result() == "foo"
+
+
+class TestStateGroup:
+    def test_fail_count(self):
+        states = [
+            Failed(data=ValueError("1")),
+            Failed(data=ValueError("2")),
+            Failed(data=ValueError("3")),
+            Crashed(data=ValueError("4")),
+            Crashed(data=ValueError("5")),
+        ]
+
+        assert StateGroup(states).fail_count == 5
+
+    def test_all_completed(self):
+        states = [
+            Completed(data="test"),
+            Completed(data="test"),
+            Completed(data="test"),
+        ]
+
+        assert StateGroup(states).all_completed()
+
+        states = [
+            Completed(data="test"),
+            Failed(data=ValueError("1")),
+        ]
+
+        assert not StateGroup(states).all_completed()
+
+    def test_any_failed(self):
+        states = [
+            Completed(data="test"),
+            Failed(data=ValueError("1")),
+        ]
+
+        assert StateGroup(states).any_failed()
+
+        states = [
+            Completed(data="test"),
+            Completed(data="test"),
+            Completed(data="test"),
+        ]
+
+        assert not StateGroup(states).any_failed()
+
+    def test_all_final(self):
+        states = [
+            Failed(data=ValueError("failed")),
+            Crashed(data=ValueError("crashed")),
+            Completed(data="complete"),
+            Cancelled(data="cancelled"),
+        ]
+
+        assert StateGroup(states).all_final()
+
+        states = [
+            Failed(data=ValueError("failed")),
+            Crashed(data=ValueError("crashed")),
+            Completed(data="complete"),
+            Cancelled(data="cancelled"),
+            Running(),
+        ]
+
+        assert not StateGroup(states).all_final()
+
+    def test_counts_message_all_final(self):
+        states = [
+            Failed(data=ValueError("failed")),
+            Crashed(data=ValueError("crashed")),
+            Completed(data="complete"),
+            Cancelled(data="cancelled"),
+        ]
+
+        counts_message = StateGroup(states).counts_message()
+
+        assert "total=4" in counts_message
+        assert "'FAILED'=1" in counts_message
+        assert "'CRASHED'=1" in counts_message
+        assert "'COMPLETED'=1" in counts_message
+        assert "'CANCELLED'=1" in counts_message
+
+    def test_counts_message_some_non_final(self):
+        states = [
+            Failed(data=ValueError("failed")),
+            Running(),
+            Crashed(data=ValueError("crashed")),
+            Running(),
+        ]
+
+        counts_message = StateGroup(states).counts_message()
+
+        assert "total=4" in counts_message
+        assert "not_final=2" in counts_message
+        assert "'FAILED'=1" in counts_message
+        assert "'CRASHED'=1" in counts_message
+        assert "'RUNNING'=2" in counts_message