The problem is very un-SQL-like, you're assigning status before you compare it;
status=VALUES(status), -- assign so they're equal
activated=IF(status <> VALUES(status) AND -- compare, always equal
VALUES(status) = 'active', NOW(), activated);
Just reverse the assignment and thing should work fine.
INSERT INTO test
(`id`, `title`, `status`, `activated`)
VALUES
(:id, :title, :status, NULL)
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id),
activated=IF(status <> VALUES(status) AND
VALUES(status) = 'active', NOW(), activated),
status=VALUES(status);
Btw, you only need to assign columns that actually change, no need to set id
on update.