How I tested *every* authorisation, authorised or not
Well… I didn’t, not down to bit level. But I got closer than any time before as every business case was tested.
Don’t believe my rudimentary text below will simply answer how to do it; it will just give a raw landscape. It took me several nights and 3 iterations before I solved all the tidbits.
More important than to test that every Role could get to its authorised Product was to see that it could not get to unauthorised ditto.
So I had to test every combination.
Testing every permutation of User, Role and Product was not feasible. Each entity has several properties and they are, mostly, not related to authorisation. Then we have 2^64 kinds of userId where most of them are not interesting and not even in use. To continue a test on “Role of type X or Y” is really a “Role is HiredInProductsCompany”.
So I sat down and extracted the if statements from the code. They were like “if loggedOn” and “if in Role x or y”. Every such statement was extracted as a method and moved into a (temporary) helper lib.
When I was sure, with visual inspection, I had caught everything, I put some business logic into thought and manipulated and rearranged the methods. They became fewer and matchable to business requirements. Gone was “if user.Role == Administrator and user.Company = product.Company” but instead “if user.IsAdministratorAtProductsCompany(product)”.
Note that during this process I have not changed any logic and, testing besides, present state could be shipped all the time.
Now I had to get rid of any technical remains. On the outside it looked ok as the method names where very descriptive in business lingua but inside the authorisation method was “if user.Id == 0” or “if challengedUser.Id == persistedUser.ID”. It was not usable since Id as integer is a technical (often a persistance layer construction) solution for recognising an entity. In business terms it is more like “user.IsPersisted” and “if challengedUser.SameAs(persistedUser)”. I continued redusing the problem space to what I really wanted to test when authorising.
This way I seriously minimised the permutations, as an authorisable User did not care about “Id” or “Name” or “BusinessPartner” but only “IsLoggedOn” and “Role”. With 6 roles that means 12 permutations. With Project I came to, say, 32 permutations and user 4. This gives in all 12*32*4=1500 variants. Not a problem to test every combination now if I just put some (business) intelligence into creating the tests. #win
Let’s say 240 of them were positive (autorised) and the rest negative.
I started with creating a simple lib for permutating every possible input and them through One Test to green light “not authorised”. Everything red should be authorised. Already here I might have found combinations that was authorised when they should not have been.
Then I, manually, created a list of every permutation allowing authorisation. Well… manually for a programmer is reducing to loops and ifs so one method could create every authorised combination of type A and one of type B. Alltogether that is, say, 10 different methods and some manual ones. They were concatenated to a list and I made the Test assert authorised and not-authorised according to this list.
So now I had a test for every kind of authorisation check testing both authorised and not authorised and tests looks like business logic.