We have been fuzzing our Rails applications to see what breaks, and finding some interesting things. This week, the continuous integration build started to break at random, but only on one application. Pull out the Yak clippers!
By looking at the test logs, we quickly isolated the problem. Rails date helpers let users input bad dates, e.g. February 30, 2008. (In almost all scenarios, we avoid the built-in date helpers anyway. But almost only works for horseshoes and hand grenades, and I don't want special cases that break.)
Obviously our fuzz generator will generate bad dates occasionally. Before you read further, test yourself: How does ActiveRecord handle such dates? How should it? The answer is in ActiveRecord::Base's execute_callstack_for_multiparameter_attributes, and it surprised me:
Timeclass, the day overflows into the next month, so 2/29 magically becomes 3/1. Yuck.
Date, Ruby raises an error. Rails then wraps this error, and by the time it gets back to you the offending values are not easily accessible.
Neither of these approaches appeal to me. A bad date should be validation error. Matthew pointed me to a plugin that lets you, on a per model basis, monkey patch ActiveRecord to report bad
Dates as validation errors.
I decided that I wanted to have a patch that overrode Rails' behavior for all models, so I have written the first_class_dates plugin. This gets our tests passing again, and it keeps bad data from raising application exceptions, but it is still a hack. A better solution would:
Daterefuses even to enter a bad value state.)
The better solution would require a more substantial patch, and should be done in Rails itself. If others agree with the approach, we will submit the patch.